HTTPS와 TLS 핸드셰이크 — 처음 만난 둘이 어떻게 비밀을 나누나
DNS로 IP를 찾은 다음, 브라우저와 서버는 어떻게 안전한 통신 터널을 만드는가. 대칭·비대칭 암호의 조합, TLS 핸드셰이크의 단계, 인증서와 CA로 상대를 신뢰하는 방법을 정리했다.
HTTPS와 TLS 핸드셰이크
DNS, IP, NAT의 기초에서 도메인을 IP로 바꿔 서버의 위치를 찾았다. 그런데 위치를 안다고 끝이 아니다. 그 길은 공유기, ISP, 수많은 중계 장비를 지나는 공용 도로다. 누군가 중간에서 엿보거나(도청), 내용을 바꾸거나(변조), 서버인 척 위장할 수 있다.
HTTPS는 이 길 위에 암호화된 터널을 깐다. 그런데 여기엔 묘한 문제가 있다. 한 번도 만난 적 없는 브라우저와 서버가, 도청당하는 공개된 길에서, 어떻게 둘만 아는 비밀 키를 나눌 수 있을까. TLS 핸드셰이크가 바로 그 문제를 푸는 절차다.
이 글은 browser-secret-decoding에서 본 Web Crypto(AES, RSA)가 실제로 쓰이는 현장이기도 하다.
HTTP는 평문이다
기본 HTTP는 내용을 그대로 보낸다. 같은 네트워크에 있는 사람이나 중간 경로의 장비는 주고받는 내용을 다 들여다볼 수 있다. 위협은 세 가지다.
- 도청(기밀성 깨짐): 비밀번호, 쿠키, 결제 정보가 평문으로 흐른다.
- 변조(무결성 깨짐): 중간에서 응답을 바꿔치기할 수 있다. 광고 삽입, 악성코드 주입.
- 위장(인증 실패): 내가 접속한 곳이 진짜 그 서버인지 알 수 없다.
HTTPS는 TLS(Transport Layer Security)를 입혀 이 셋을 동시에 해결한다. 즉 기밀성(암호화) + 무결성(변조 감지) + 인증(상대 확인)이다.
핵심 난제 — 키를 어떻게 나누나
암호화에는 두 종류가 있고, 둘 다 한계가 있다.
| 대칭키 (예: AES) | 비대칭키 (예: RSA, ECDHE) | |
|---|---|---|
| 키 구조 | 암호화·복호화에 같은 키 | 공개키 + 개인키 쌍 |
| 속도 | 빠름 | 느림 |
| 문제 | 그 키를 어떻게 안전하게 전달? | 대량 데이터엔 너무 느림 |
대칭키(AES)는 빠르지만, 그 키를 상대에게 보내야 하는데 평문 길로 보내면 도청당한다. 비대칭키는 공개키로 암호화하면 개인키로만 풀 수 있어 키 전달 문제가 없지만, 모든 통신을 비대칭으로 하기엔 너무 느리다.
그래서 TLS는 둘을 조합한다.
- 처음에 비대칭 방식으로 "대칭 키"를 안전하게 합의한다.
- 합의가 끝나면 그 다음부터는 빠른 대칭키(AES)로 실제 데이터를 주고받는다.
비대칭은 "키를 나누는 의식"에만 쓰고, 본 통신은 대칭으로 한다. 이 하이브리드가 TLS의 핵심 아이디어다.
TLS 핸드셰이크 흐름
대략의 흐름은 이렇다. (TLS 1.2 기준으로 단계를 보이고, 1.3의 단축은 뒤에서 언급한다.)
브라우저(Client) 서버(Server)
│ ① ClientHello │
│ - 지원하는 암호 스위트 목록 │
│ - 클라이언트 랜덤값 │
│ ───────────────────────────────────────▶│
│ │
│ ② ServerHello │
│ - 선택된 암호 스위트 │
│ - 서버 랜덤값 │
│ - 인증서(서버 공개키 포함) │
│ ◀───────────────────────────────────────│
│ │
│ ③ 인증서 검증 (CA 체인 확인) │
│ ④ 키 교환 — 양쪽이 같은 세션키(대칭) 도출 │
│ ◀──────────────────────────────────────▶│
│ │
│ ⑤ Finished — 이후 통신은 세션키(AES)로 │
│ ◀══════════ 암호화된 데이터 ════════════▶│- ① ClientHello: 브라우저가 "이런 암호 알고리즘들 쓸 수 있어" 목록과 랜덤값을 보낸다.
- ② ServerHello: 서버가 그중 하나를 고르고, 랜덤값과 인증서(자기 공개키가 든)를 보낸다.
- ③ 검증: 브라우저가 인증서가 진짜인지 확인한다. (다음 절)
- ④ 키 교환: 양쪽의 랜덤값과 키 교환 결과를 조합해 동일한 세션 키(대칭)를 각자 계산한다. 옛 방식은 RSA로 키를 암호화해 보냈고, 현대(ECDHE) 방식은 키 자체를 보내지 않고 양쪽이 따로 같은 값을 도출한다(전방향 비밀성 제공).
- ⑤ Finished: 이후 모든 데이터는 이 세션 키로 대칭 암호화된다.
TLS 1.3은 이 과정을 정리해 1-RTT(왕복 한 번)로 단축했고, 취약한 옛 RSA 키 교환을 빼고 ECDHE 중심으로 바꿨다. 그래서 더 빠르고 안전하다.
인증서와 CA — 공개키가 진짜 그 서버 것인가
핸드셰이크에 빈틈이 하나 있다. 서버가 공개키를 보내준다 해도, 중간자가 가로채서 자기 공개키를 서버 것인 척 끼워 넣으면? 브라우저는 중간자와 비밀 키를 나눠버리고, 중간자는 모든 걸 복호화한다. 이게 중간자 공격(MITM)이다.
이를 막는 것이 인증서(Certificate)와 CA(Certificate Authority, 인증기관)다.
신뢰 체인:
Root CA ──서명──▶ 중간 CA ──서명──▶ 서버 인증서(naver.com, 공개키)
브라우저는 OS/브라우저에 내장된 "신뢰하는 Root CA 목록"을 가지고,
서버 인증서 → 중간 CA → Root CA로 이어지는 서명 체인을 검증한다.- 서버는 신뢰받는 CA에게 "이 공개키는 naver.com 것"이라고 서명받은 인증서를 받아 둔다.
- 브라우저는 인증서의 서명을 CA의 공개키로 검증하고, 인증서에 적힌 도메인이 실제 접속 도메인과 같은지 확인한다.
- 하나라도 어긋나면 그 유명한 "이 연결은 안전하지 않습니다" 경고가 뜬다.
즉 "공개키를 믿어도 되는가"라는 문제를, "이미 신뢰하는 CA가 보증했는가"로 바꿔서 푼 것이다.
Web Crypto와의 연결
browser-secret-decoding에서 본 crypto.subtle의 알고리즘들이 바로 이 핸드셰이크의 부품이다.
- RSA-OAEP / ECDH: 키 교환(④)에 쓰는 비대칭 방식.
- AES-GCM: 합의된 세션 키로 본 통신을 암호화하는 대칭 방식.
- SHA / HMAC: 무결성 검증과 서명에 쓰는 해시.
브라우저가 HTTPS에서 자동으로 하는 일을, 애플리케이션 코드에서 직접 할 수 있게 노출한 것이 Web Crypto API다. 그래서 crypto.subtle이 HTTPS(보안 컨텍스트)에서만 동작하는 것도 자연스럽다.
정리
- HTTP는 평문이라 도청·변조·위장에 취약하다. HTTPS는 TLS로 기밀성·무결성·인증을 더한다.
- 대칭키는 빠르지만 키 전달이 문제, 비대칭키는 키 전달은 풀지만 느리다. TLS는 비대칭으로 대칭 키를 합의하고, 이후엔 대칭으로 통신하는 하이브리드다.
- 핸드셰이크는 ClientHello → ServerHello(+인증서) → 검증 → 키 교환 → Finished 순으로, 양쪽이 같은 세션 키를 만든다. TLS 1.3은 1-RTT로 단축됐다.
- 공개키를 믿을 수 있는 이유는 CA가 서명한 인증서와 브라우저의 신뢰 Root 목록 덕분이다.
- 이 모든 암호 부품(RSA/ECDH/AES/SHA)은 Web Crypto의
crypto.subtle로도 직접 만질 수 있다.
DNS가 "어디로 갈지"를 정했다면, TLS는 "안전하게 가는 길"을 깐다. 다음은 그 길 위에서 실제로 주고받는 약속, HTTP의 기초다.
참고 문서
Related Posts
같이 읽으면 좋은 글
인증과 인가 — 세션-쿠키 방식과 JWT 토큰 방식
HTTP는 무상태인데 어떻게 로그인 상태를 유지하는가. 인증과 인가의 차이, 서버가 상태를 갖는 세션-쿠키 방식과 토큰에 정보를 담는 JWT 방식의 트레이드오프, 그리고 토큰을 어디에 둘지를 정리했다.
DNS 레코드와 CDN — A 레코드 한 줄 뒤에 숨은 것들
DNS는 도메인을 IP로 바꾸기만 하는 게 아니다. A·AAAA·CNAME·MX·TXT 같은 레코드의 역할과 TTL, 그리고 같은 도메인이 사용자마다 다른 서버로 연결되는 CDN·GeoDNS의 원리를 정리했다.