개인 웹사이트 보안 강화: 클라이언트 인증서로 나만의 보안 웹사이트 설정

목차

아무도 접근할 수 없는 나만의 보안 웹사이트로 설정하세요.
중요한 정보를 지기키 위해 반드시 필요합니다.

Apache 웹서버에 클라이언트 인증서 인증을 구현하여 지정된 기기에서만 웹사이트 접속을 허용하는 방법을 단계별로 안내합니다.

목적

요즘은 웹어플리케이션 만들기가 참 쉽습니다.
오픈소스를 이용해도 되고, 웹호스팅도 무료인 것이 많죠.
아니면 서버를 아예 내가 구축할 수도 있습니다.

그런데 요즘은 해킹 기술도 워낙 좋아져서
나 혼자 사용하는 나만의 비밀 정보를 저장하는 웹사이트를 인터넷에 서비스하는게 불안하죠.
아이디/패스워드로 보안을 설정한다고해도 여전히 불안합니다.

ACL을 설정하여 특정 IP에서만 접속이 가능하도록 하면
정보보안은 아주 강화되지만
웹어플리케이션이라는 특성을 살릴 수 없어서 내가 많이 불편해 집니다.

이럴 때는 클라이언트 인증서(Client Certificate)를 사용할 수 있습니다.
마치 ACL과 같이 인증서를 사전에 등록한 디바이스(웹브라우저)에서만 웹사이트에 접속할 수 있게되죠.

나 혼자만 사용하는 보안 웹사이트를 운영할 때 제격입니다.
이 포스팅은 제가 다음에 서버 설정할 때 참고할 수 있도록
제가 서버 설정하였던 과정을 기록합니다.

구현 예정 보안 구조

  • 1차 인증: 클라이언트 SSL 인증서 (Apache SSL/TLS 레벨)
  • 2차 인증: ID/PW 로그인 (애플리케이션 레벨)
  • 결과: 인증서가 없으면 웹사이트 접속 자체가 불가능

이 방법은 은행이나 금융기관에서 사용하는 수준의 보안 수준으로
공격자는 인증서 파일과 비밀번호를 모두 탈취해야만 개인 웹사이트에 접속이 가능합니다.

서버 환경

  • 서버: Rocky Linux 9.7
  • 웹서버: Apache 2.4
  • 도메인: sample-website.com (예시)
  • 클라이언트: macOS Safari, Chrome, iPhone Safari

클라이언트 인증서 생성

작업 디렉토리

sudo mkdir -p /etc/httpd/client-certs
cd /etc/httpd/client-certs

CA(Certificate Authority) 개인키 생성

sudo openssl genrsa -aes256 -out ca-key.pem 4096

비밀번호를 입력하라는 메시지가 나타나는데
앞으로 필요할 때 마다 사용하니 기억하기 쉬운 비밀번호로 설정하세요.

참고: openssl은 암호화 도구로 인증서를 생성하고 관리하는 데 사용합니다~

CA 인증서 생성

sudo openssl req -new -x509 -days 3650 -key ca-key.pem -sha256 -out ca.pem

저는 인증서 파일을 완벽하게 관리할 수 있어서 귀찮은 마음에 10년을 설정했습니다. (보안은;;;)
명령어 실행하면 여러 정보를 입력하라고 나오는데 그냥 모두 Enter 입력하고 넘어가도 됩니다.

명령어 인자 의미:

  • -days 3650: 10년 동안 유효한 인증서
  • -x509: 자체 서명 인증서 형식
  • -sha256: SHA-256 암호화 방식 사용

클라이언트 개인키 생성

sudo openssl genrsa -out client-key.pem 4096

이번에는 비밀번호 없이 그냥 Enter 입력합니다.

클라이언트 인증서 서명 요청(CSR) 생성

sudo openssl req -new -key client-key.pem -out client.csr

여기서도 정보 입력하는 화면이 나오는데 그냥 모두 Enter 입력하고 넘어갑니다.

CA로 클라이언트 인증서 서명

sudo openssl x509 -req -days 3650 -in client.csr -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out client-cert.pem -sha256

이 명령어 실행할 때 아까 CA 개인키 설정할 때 지정한 비밀번호를 입력합니다.

역시나 귀찮은 마음에 기간은 10년.
(10년동안 절대 잃어버리지 말 것!)

브라우저용 PKCS12 파일 생성

sudo openssl pkcs12 -export -out client.p12 -inkey client-key.pem -in client-cert.pem -certfile ca.pem

나중에 웹브라우저에 인증서 설치할 때 입력할 비밀번호를 여기에서 설정합니다.
기억할 수 있는 비밀번호로 지정하세요~

생성된 파일 확인

자, 이제 다음과 같은 파일들이 생성되었습ㄴ디ㅏ.

  • ca-key.pem: CA 개인키 (유출 금지!)
  • ca.pem: CA 인증서 (Apache 설정용)
  • client-key.pem: 클라이언트 개인키
  • client-cert.pem: 클라이언트 인증서
  • client.csr: 인증서 서명 요청
  • client.p12: 브라우저 설치용 파일

Apache 설정

SSL 설정 파일 수정

https 접속 시 반드시 클라이언트 인증서를 확인하도록 SSL 설정 부분을 변경합니다.

sudo vi /etc/httpd/conf.d/sample-website.com-ssl.conf

기존 conf 파일에서 SSLCertificateKeyFile 줄 아래에 다음의 3줄을 추가합니다.

SSLCACertificateFile /etc/httpd/client-certs/ca.pem
SSLVerifyClient require
SSLVerifyDepth 2

참고로 설정의 의미는 다음과 같습니다.

  • SSLCACertificateFile: CA 인증서 위치 설정
  • SSLVerifyClient require: 클라이언트 인증서 필수로 지정
  • SSLVerifyDepth 2: 인증서 체인 검증 깊이

수정 후 conf 파일 내용
conf 내용은 웹서버마다 다르니 아래 내용은 참고만 하세요~

<VirtualHost *:443>
    ServerName sample-website.com
    ServerAlias www.sample-website.com
    DocumentRoot /var/www/vhosts/sample-website.com/public_html
    SSLEngine on
    SSLCertificateFile /etc/letsencrypt/live/sample-website.com/fullchain.pem
    SSLCertificateKeyFile /etc/letsencrypt/live/sample-website.com/privkey.pem
    
    # 클라이언트 인증서 설정
    SSLCACertificateFile /etc/httpd/client-certs/ca.pem
    SSLVerifyClient require
    SSLVerifyDepth 2
    
    <Directory "/var/www/vhosts/sample-website.com/public_html">
        AllowOverride All
        Require all granted
    </Directory>
    ErrorLog /var/log/httpd/sample-website.com_ssl_error.log
    CustomLog /var/log/httpd/sample-website.com_ssl_access.log combined
</VirtualHost>

Apache 설정 확인

자, 이제 Apache 설정ㅇ르 검토하고 Apache를 재시작 합니다.

sudo apachectl configtest
sudo systemctl restart httpd
sudo systemctl status httpd --no-pager

Syntax OK가 나와야 하고
active (running) 상태인지 확인합니다.

클라이언트 인증서 설치

client.p12 파일 복사

FTP 클라이언트 등을 이용하여 생성한 파일 중 client.p12 파일을 컴퓨터로 전송합니다.
파일 전송을 위해서는 파일 권한이나 소유자를 변경해야 할 수 있습니다.

sudo chmod 644 /etc/httpd/client-certs/client.p12

그리고 다운로드 받은 p12 파일의 파일명을 기억하기 쉬운 파일명으로 수정하세요.
나중에 시간이 지나서 이 파일을 보면 “이게 무슨 파일이었지??“라고 기억 안 날 수 있습니다.

이제 컴퓨터가 바뀌거나 웹브라우저를 변경할 때 이 파일만 등록하면 됩니다.

(예시) macOS에 설치

  1. 다운로드 한 client.p12 파일을 더블클릭
  2. “키체인 접근” 앱이 자동으로 열림 (필요시 macOS 패스워드 입력)
  3. 앞서 설정하였던 웹브라우저용 비밀번호 입력
  4. “로그인” 키체인에 추가 완료
  5. “키체인 접근” 앱 실행
  6. 왼쪽에서 “로그인” 선택
  7. “나의 인증서” 카테고리 클릭
  8. “Default Company Ltd” 인증서를 더블클릭
  9. “Trust” (신뢰) 섹션 펼치기
  10. “When using this certificate"를 “Always Trust"로 변경
  11. 설정 완료

(예시) 아이폰에 설치

  1. client.p12 파일을 iCloud, AirDrop 방법 등으로 아이폰으로 옮깁니다.
  2. iPhone “파일” 앱에서 client.p12 파일 실행
  3. “프로파일 다운로드됨” 알림 확인
  4. 설정 앱으로 이동
  5. “프로파일” → “설치”
  6. 비밀번호 입력
  7. 설정 완료

테스트 및 검증

이제 다 됐습니다!
웹브라우저를 열어서 웹사이트에 접속해 봅시다.
인증서 확인이 자동으로 뜰 것입니다.

인증서 있는 기기에서 접속 시

macOS Safari 또는 Chrome에서 웹사이트에 접속

https://sample-website.com
  • 인증서 선택 팝업이 나타납니다.
  • “Default Company Ltd” 선택합니다.
  • “Allow” 또는 “Continue” 클릭합니다.
  • 웹사이트가 정상적으로 열립니다.

인증서 없는 기기에서 접속 시

정말 웹사이트 접속을 차단하는지 확인하기 위해 다른 컴퓨터로 접속해 봅시다.

https://sample-website.com
  • “This website requires a certificate” 메시지가 나타납니다.
  • 선택 가능한 인증서 없음
  • 접속이 차단되어 화면에 아무것도 안 뜹ㄴ디ㅏ.

CURL 명령어로 명확하게 확인

웹사이트와 핸드쉐이크 과정이 어떻게 진행되는지 확인하기 위해
인증서 없는 컴퓨터에서 CMD 창을 열고 다음 명령어를 입력합니다.

curl -v https://sample-website.com

그러면 내용 중 아래와 같은 메시지가 나타나면서
웹사이트 핸드쉐이크 과정 중에 접속이 차단되어 연결이 완전히 불가능함을 알 수 있습니다.

SSL handshake failed
SEC_E_ILLEGAL_MESSAGE

최종 결과

구현된 보안 구조

1차 방어: 클라이언트 인증서 (SSL/TLS 레벨)

  • 인증서 없으면 연결 자체가 차단
  • 웹페이지, 로그인 화면조차 볼 수 없음
  • 10년 유효 (나만의 웹사이트이니 편의상)

2차 방어: ID/PW 인증 (애플리케이션 레벨)

  • 인증서가 있어도 로그인 필요
  • 이중 인증 완성

HTTP 활용과 보안에 관하여

저는 80포트는 완전히 서비스 하지 않지만
특별히 Let’s Encrypt 인증서를 자동갱신하기 위해 아래와 같이 설정합니다.

<VirtualHost *:80>
    ServerName sample-website.com
    
    # Let's Encrypt 인증서 갱신을 위한 경로는 허용
    Alias /.well-known/acme-challenge/ /var/www/html/.well-known/acme-challenge/
    <Directory "/var/www/html/.well-known/acme-challenge/">
        Require all granted
    </Directory>
    
    # 나머지는 HTTPS로 리다이렉트
    RewriteEngine On
    RewriteCond %{REQUEST_URI} !^/\.well-known/acme-challenge/
    RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
</VirtualHost>

이렇게 설정하면 80 포트로 Let’s Encrypt도 잘 동작하고
나머지 경로에 대해서는 모두 강제로 https 리다이렉트 하기 때문에
웹브라우저 인증서가 없다면 연결이 차단됩니다.

마무리

이렇게 웹사이트를 설정하면 클라이언트 인증서만 유출되지 않게 잘 보관하면서
컴퓨터를 바꿀 때 마다 내 웹브라우저에 등록하면 됩니다.

혹시나 클라이언트 인증서를 잃어버린 경우
그냥 위 과정을 다시 반복해서 새로 만들면 됩니다.

이렇게 공격자가 나의 민감한 데이터에 접근하지 못하도록
웹사이트 접근 단계에서부터 완벽하게 차단할 수 있습니니다.

참 쉽죠? 끝.