HTTPS와 TLS의 실제 작동 원리: 핸드셰이크와 인증서
HTTPS가 중요한 이유 (그리고 왜 오해되는가)
브라우저에서 자물쇠 아이콘을 보고 사이트가 "안전하다"고 생각해 본 적이 있을 것입니다. 하지만 그것이 정확히 무엇을 의미할까요? HTTPS는 단순한 암호화가 아니라 신원, 무결성, 신뢰에 관한 것입니다. 많은 개발자들이 TLS를 블랙박스처럼 취급합니다—한 번 설정하고 잊어버립니다. 그로 인해 잘못된 구성, 만료된 인증서, 취약한 암호화 스위트가 발생합니다.
이 글에서는 핸드셰이크부터 인증서 검증까지 HTTPS와 TLS가 실제로 어떻게 작동하는지 분석하고 올바르게 설정하는 방법을 보여드립니다. 군더더기 없이 오늘 바로 적용할 수 있는 실용적인 지식만 전달합니다.
TLS 핸드셰이크: 첫 바이트 전에 일어나는 일
클라이언트(브라우저)가 HTTPS를 통해 서버에 연결하면 TLS 핸드셰이크를 수행합니다. 이는 보안 채널을 설정합니다. 다음은 간단히 정리한 버전입니다(TLS 1.3):
- 클라이언트 헬로: 클라이언트가 지원하는 암호화 스위트, 임의 값, 키 공유(키 교환용)를 보냅니다.
- 서버 헬로: 서버가 암호화 스위트를 선택하고 자체 임의 값과 키 공유를 보내며 인증서 체인을 제시합니다.
- 인증: 클라이언트가 신뢰할 수 있는 CA에 대해 서버 인증서를 검증하고 호스트 이름을 확인합니다.
- 키 유도: 양측이 ECDHE(타원 곡선 디피-헬만 임시)를 사용하여 공유 비밀을 계산합니다.
- 완료: 양측이 암호화된 "완료" 메시지를 보내 핸드셰이크를 확인합니다.
이후 애플리케이션 데이터는 대칭 키로 암호화됩니다. TLS 1.3은 왕복 횟수를 1회로 줄여 지연 시간을 개선합니다.
키 교환: ECDHE vs RSA
구버전 TLS는 순방향 비밀성을 제공하지 않는 RSA 키 교환을 사용했습니다. 서버의 개인 키가 유출되면 과거 트래픽을 복호화할 수 있습니다. 현대적인 설정은 각 세션마다 고유한 키를 제공하는 순방향 비밀성을 갖춘 ECDHE를 사용합니다.
# 예: 지원되는 키 교환 알고리즘 확인
openssl s_client -connect example.com:443 -cipher 'ECDHE'
인증서: 신뢰의 닻
인증서는 공개 키를 신원(도메인 이름)에 바인딩합니다. 클라이언트가 신뢰하는 인증 기관(CA)이 서명합니다. 브라우저에는 신뢰할 수 있는 루트 CA 저장소가 포함되어 있습니다.
사이트를 방문하면 서버가 체인을 보냅니다: 리프 인증서(도메인용) → 중간 CA → 루트 CA. 클라이언트는 신뢰할 수 있는 루트까지 각 서명을 검증합니다.
인증서 유형
- 도메인 검증(DV): 도메인 제어만 증명합니다. 빠르고 무료입니다(예: Let's Encrypt).
- 조직 검증(OV): 검증된 조직 세부 정보를 포함합니다.
- 확장 검증(EV): 더 엄격한 심사; 브라우저는 더 이상 녹색 표시줄을 표시하지 않지만 일부 기업은 여전히 사용합니다.
인증서 검증 단계
브라우저가 확인하는 사항:
- 서명 체인이 신뢰할 수 있는 루트로 이어지는지.
- 인증서가 유효 기간 내에 있는지.
- 호스트 이름이 일치하는지(주체 대체 이름).
- 인증서가 폐기되지 않았는지(OCSP 또는 CRL을 통해).
어떤 검사라도 실패하면 경고가 표시됩니다. 프로덕션에서는 절대 이러한 경고를 무시하지 마세요.
서버에서 HTTPS 구성하기
현대적인 TLS 설정을 사용하는 일반적인 Nginx HTTPS 구성을 살펴보겠습니다.
server {
listen 443 ssl http2;
server_name example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384';
ssl_prefer_server_ciphers off;
# HSTS (주의해서 사용)
add_header Strict-Transport-Security "max-age=63072000" always;
}
참고:
- TLS 1.2 및 1.3만 사용하고 이전 버전은 비활성화하세요.
- 순방향 비밀성을 위해 ECDHE 암호화를 선호하세요.
- 성능을 위해 HTTP/2를 활성화하세요.
- HSTS는 브라우저가 HTTPS를 사용하도록 강제합니다. 테스트 후에만 활성화하세요.
Let's Encrypt로 인증서 얻기
Let's Encrypt는 무료 DV 인증서를 제공합니다. Certbot을 사용하여 발급 및 갱신을 자동화하세요:
sudo certbot --nginx -d example.com -d www.example.com
Certbot은 Nginx 구성을 수정하고 갱신을 위한 cron 작업을 설정합니다. 항상 --dry-run으로 갱신을 테스트하세요.
흔한 실수와 피하는 방법
- 만료된 인증서: 모니터링과 자동 갱신을 설정하세요.
- 불완전한 체인: 중간 인증서를 포함하세요(fullchain.pem).
- 취약한 암호화: RC4, DES, export 암호화를 비활성화하세요.
- 혼합 콘텐츠: 모든 리소스(이미지, 스크립트)가 HTTPS로 로드되는지 확인하세요.
- HSTS 없음: HSTS가 없으면 사용자가 HTTP로 다운그레이드될 수 있습니다.
HTTPS 구성 테스트하기
SSL Labs와 같은 온라인 도구나 명령줄 도구를 사용하여 설정을 검증하세요.
# 인증서 세부 정보 확인
openssl s_client -connect example.com:443 -servername example.com | openssl x509 -noout -text
# TLS 1.3 지원 테스트
openssl s_client -connect example.com:443 -tls1_3
확인할 사항:
- 유효한 인증서 체인.
- 취약한 프로토콜 없음(SSLv3, TLS 1.0, TLS 1.1).
- 순방향 비밀성 활성화됨.
- HSTS 헤더 존재.
FAQ
TLS와 SSL의 차이점은 무엇인가요?
SSL은 TLS의 전신입니다. TLS 1.0은 본질적으로 SSL 3.1이었습니다. 오늘날 "SSL"은 구어적으로 자주 사용되지만 항상 TLS 1.2 또는 1.3을 사용해야 합니다.
HTTPS를 위해 유료 인증서가 필요한가요?
아니요. Let's Encrypt의 무료 DV 인증서는 모든 주요 브라우저에서 신뢰되며 유료 인증서와 동일한 암호화를 제공합니다. 유료 인증서는 보증과 지원을 제공하지만 암호화는 동일합니다.
HTTPS가 성능에 어떤 영향을 미치나요?
TLS는 핸드셰이크로 인해 지연 시간을 추가하지만 TLS 1.3은 이를 1회 왕복으로 줄입니다. 세션 재개와 HTTP/2를 사용하면 오버헤드가 최소화됩니다. 최신 CPU에서는 암호화/복호화 비용이 낮습니다.
서버의 TLS 구성을 점검할 준비가 되셨나요? Nginx 로그 분석기를 사용하여 비정상적인 트래픽 패턴을 발견하고 HTTPS 설정이 예상대로 작동하는지 확인하세요.