10장 : HTTPS와 네트워크 보안
TLS 핸드셰이크
암호화의 기본 개념을 잡았으니, 이제 HTTPS 연결이 실제로 어떻게 수립되는지를 따라가 보겠습니다. 브라우저가 https:// URL에 접속할 때 TCP 3-way handshake 직후, 데이터를 교환하기 전에 TLS 핸드셰이크가 이루어집니다.
TLS 1.2 핸드셰이크 전체 흐름
클라이언트 서버
│ │
│── ① Client Hello ────────────────────────→│
│ * TLS 버전: 1.2 │
│ * 지원 Cipher Suite 목록 │
│ * 클라이언트 랜덤 (28바이트) │
│ │
│←─ ② Server Hello ─────────────────────────│
│ * 선택된 TLS 버전: 1.2 │
│ * 선택된 Cipher Suite │
│ * 서버 랜덤 (28바이트) │
│ │
│←─ ③ Certificate ──────────────────────────│
│ * 서버 인증서 (공개키 포함) │
│ * 인증서 체인 (중간 CA 포함) │
│ │
│←─ ④ Server Key Exchange ──────────────────│
│ * ECDHE 파라미터 (곡선, 서버 공개값) │
│ * 파라미터에 대한 디지털 서명 │
│ │
│←─ ⑤ Server Hello Done ────────────────────│
│ │
│── ⑥ Client Key Exchange ─────────────────→│
│ * 클라이언트 ECDHE 공개값 │
│ │
│ [양쪽: Pre-Master Secret 독립 계산] │
│ [양쪽: Master Secret 생성] │
│ [양쪽: 세션 키(대칭키) 생성] │
│ │
│── ⑦ Change Cipher Spec ──────────────────→│
│── ⑧ Finished (암호화됨) ─────────────────→│
│ │
│←─ ⑨ Change Cipher Spec ───────────────────│
│←─ ⑩ Finished (암호화됨) ──────────────────│
│ │
│←═══════ 암호화된 통신 시작 ═══════════════→│이 전체 과정에 2 RTT가 소요됩니다. TCP 핸드셰이크(1 RTT)까지 합하면 첫 데이터 전송까지 3 RTT가 필요합니다.
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
│ │ │ │ │ │ │
│ │ │ │ │ │ └── 해시: SHA-384
│ │ │ │ │ └─────── 암호 모드: GCM
│ │ │ │ └──────────── 키 길이: 256비트
│ │ │ └───────────────── 대칭 암호: AES
│ │ └───────────────────────── 인증: RSA
│ └──────────────────────────────── 키 교환: ECDHE
└────────────────────────────────────── 프로토콜: TLS| 구성 요소 | 역할 | TLS 1.2 옵션 | TLS 1.3 옵션 |
|---|---|---|---|
| 키 교환 | 대칭키 교환 | RSA, DHE, ECDHE | ECDHE만 |
| 인증 | 서버 신원 | RSA, ECDSA | RSA, ECDSA |
| 대칭 암호 | 데이터 암호화 | AES-CBC, AES-GCM... | AES-GCM, ChaCha20 |
| 해시 | 무결성 | SHA-1, SHA-256... | SHA-256, SHA-384 |
디피-헬만 키 교환
TLS에서 가장 중요한 단계가 키 교환입니다. ECDHE(Elliptic Curve Diffie-Hellman Ephemeral)가 현재 표준입니다.
Alice와 Bob이 공개 채널에서 비밀 색(=키)을 합의하는 방법:
공개 정보: 노란색 (누구나 앎)
Alice:
비밀색 = 빨간색
노란색 + 빨간색 = 주황색 ──공개──→ Bob에게 전송
Bob:
비밀색 = 파란색
노란색 + 파란색 = 초록색 ──공개──→ Alice에게 전송
Alice:
Bob의 초록색 + Alice의 빨간색 = 갈색 ✓
Bob:
Alice의 주황색 + Bob의 파란색 = 갈색 ✓
도청자:
주황색과 초록색만 봄
노란색도 앎
하지만 빨간색/파란색 없이 갈색을 만들 수 없음!
수학적으로
Alice: g^a mod p → 공개
Bob: g^b mod p → 공개
공유키: g^(a*b) mod p (양쪽 동일하게 계산 가능)
공격자: g^a, g^b만으로 g^(a*b) 계산 불가 (이산 로그 문제)ECDHE의 E(Ephemeral)는 매 연결마다 새로운 키 쌍을 사용한다는 뜻입니다. 이것이 전방 비밀성(Forward Secrecy)을 보장합니다.
RSA 키 교환 (전방 비밀성 없음)
서버의 RSA 개인키가 유출되면?
→ 과거에 녹화했던 모든 트래픽 복호화 가능!
→ 공격자가 트래픽을 미리 저장해뒀다면 전부 노출
ECDHE (전방 비밀성 있음)
서버의 개인키가 유출되어도?
→ 각 세션의 임시 키는 이미 폐기됨
→ 과거 트래픽 복호화 불가!
→ "지금 뚫려도 과거는 안전"TLS 1.3의 개선점
TLS 1.3은 2018년에 표준화되었으며, TLS 1.2에 비해 보안과 성능 모두에서 큰 개선이 이루어졌습니다.
클라이언트 서버
│ │
│── Client Hello ──────────────────────────→│
│ + 지원 Cipher 목록 │
│ + 키 교환 파라미터 (미리 포함!) │
│ + SNI │
│ │
│←─ Server Hello ───────────────────────────│
│ + 선택된 Cipher │
│ + 키 교환 파라미터 │
│ + 인증서 (암호화됨!) │
│ + Finished │
│ │
│── Finished + HTTP 요청 ──────────────────→│
│ │
│ 1 RTT만에 암호화 통신 시작! │| 변경 사항 | TLS 1.2 | TLS 1.3 |
|---|---|---|
| 핸드셰이크 | 2 RTT | 1 RTT (0-RTT 재연결) |
| 키 교환 | RSA, DHE, ECDHE | ECDHE만 (전방 비밀성 필수) |
| 대칭 암호 | CBC/GCM 모두 | GCM/ChaCha20만 (AEAD 필수) |
| 해시 | SHA-1 허용 | SHA-256 이상만 |
| 인증서 | 평문 전송 | 암호화 전송 |
| 압축 | 지원 (CRIME 공격 원인) | 제거 |
| 재협상 | 지원 | 제거 (보안 취약) |
# 서버의 TLS 버전 확인
openssl s_client -connect google.com:443 -tls1_3 2>&1 | grep "Protocol"
# Protocol : TLSv1.3
# 서버가 지원하는 Cipher Suite 확인
openssl s_client -connect google.com:443 2>&1 | grep "Cipher"
# Cipher : TLS_AES_256_GCM_SHA384
# 인증서 정보 확인
openssl s_client -connect google.com:443 2>&1 | openssl x509 -noout -subject -dates
# subject=CN = *.google.com
# notBefore=Nov 13 08:38:04 2023 GMT
# notAfter=Feb 5 08:38:03 2024 GMT
# 인증서 체인 전체 출력
openssl s_client -connect google.com:443 -showcerts 2>&1 | grep "s:"인증서와 CA
TLS 핸드셰이크에서 서버가 보내는 인증서는 이 서버가 진짜 이 도메인의 소유자인지를 증명합니다.
┌──────────────────────────────────────┐
│ X.509 인증서 │
├──────────────────────────────────────┤
│ 버전: v3 │
│ 시리얼 번호: 04:A1:B2:... │
│ 서명 알고리즘: sha256WithRSA │
│ 발급자: CN=DigiCert Global CA │
│ 유효기간: 2024-01-01 ~ 2025-01-01 │
│ 주체: CN=*.example.com │
│ 공개키: RSA 2048비트 │
│ (서버의 공개키가 여기에) │
│ 확장: │
│ SAN: example.com, *.example.com │
│ Key Usage: Digital Signature │
├──────────────────────────────────────┤
│ CA의 디지털 서명 │
│ (위 내용의 해시를 CA 개인키로 암호화)│
└──────────────────────────────────────┘인증서 체인 검증
운영체제/브라우저에 미리 설치됨
↓
┌─────────────────────────┐
│ Root CA 인증서 │ Trust Anchor
│ CN=DigiCert Root CA │ 자체 서명 (Self-signed)
│ 키: RSA 4096 │
└──────────┬──────────────┘
│ 서명
┌──────────▼──────────────┐
│ Intermediate CA 인증서 │ 중간 CA
│ CN=DigiCert Global CA │ Root에 의해 서명됨
│ 키: RSA 2048 │
└──────────┬──────────────┘
│ 서명
┌──────────▼──────────────┐
│ 서버 인증서 (Leaf) │ 최종 인증서
│ CN=*.example.com │ 중간 CA에 의해 서명됨
│ 키: RSA 2048 │
└─────────────────────────┘
브라우저 검증 순서
1. 서버 인증서의 서명 → 중간 CA 공개키로 검증 ✓
2. 중간 CA 인증서의 서명 → Root CA 공개키로 검증 ✓
3. Root CA가 신뢰 저장소에 있는지 확인 ✓
4. 도메인 일치 확인 (CN/SAN vs 접속 도메인) ✓
5. 유효기간 확인 ✓
6. 폐기 여부 확인 (CRL/OCSP) ✓
→ 모두 통과하면 자물쇠 아이콘 표시왜 루트 CA가 직접 서명하지 않을까요? 루트 CA의 개인키는 오프라인 금고에 보관됩니다. 중간 CA의 키가 유출되면 그 중간 CA만 폐기하면 되지만, 루트 CA의 키가 유출되면 전체 신뢰 체계가 무너집니다.
다음 절에서는 실무에서 인증서를 관리하는 구체적인 방법을 살펴보겠습니다.