포스트

TLS/HTTPS는 어떻게 도청 불가능한 채널을 만드나 — 핸드셰이크부터 인증서 체인까지

TLS/HTTPS 핸드셰이크(TLS 1.3 1-RTT·0-RTT), 비대칭으로 대칭키를 합의하는 원리, 인증서 체인과 CA 신뢰, forward secrecy, SNI·ALPN, mTLS까지 암호화 채널의 작동을 설명합니다.

TLS/HTTPS는 어떻게 도청 불가능한 채널을 만드나 — 핸드셰이크부터 인증서 체인까지

“자물쇠 아이콘 하나에 담긴 세 가지 약속”

브라우저 주소창의 자물쇠는 세 가지를 보장합니다 — 기밀성(중간에서 못 본다), 무결성(중간에서 못 바꾼다), 인증(상대가 진짜 그 서버다). TLS(Transport Layer Security)는 이 세 가지를 평문 TCP 위에 얹는 얇은 층입니다. HTTPS = HTTP + TLS일 뿐입니다.

문제는 이걸 “암호화하니까 안전” 한 줄로 알면, 인증서 만료로 전 서비스가 멈추거나, 체인 누락으로 일부 클라이언트만 실패하거나, SNI/ALPN을 몰라 HTTP/2가 안 잡히는 사고를 못 푼다는 점입니다. 이 글은 핸드셰이크가 메시지 단위로 무슨 키를 어떻게 합의하는지, 그리고 인증서 체인이 왜 신뢰를 만드는지까지 내려갑니다.

TLS의 위치: TCP 위, HTTP 아래

TLS는 계층 관점에서 TCP(신뢰적 바이트스트림) 위에서 동작합니다. TCP가 “순서대로 안 잃고” 날라주면, TLS는 그 위에서 “암호화·인증”을 더하고, 그 위에서 HTTP가 평문처럼 동작합니다. 그래서 애플리케이션은 TLS를 거의 의식하지 않습니다.

flowchart TD
    A[HTTP 요청/응답] --> B["TLS 레코드 계층<br/>암호화 + MAC(무결성)"]
    B --> C["TCP<br/>순서·신뢰 보장"]
    C --> D[IP / 네트워크]

핵심 트릭: 비대칭으로 합의하고, 대칭으로 일한다

대칭키 암호(AES)는 빠르지만, 양쪽이 같은 키를 미리 공유해야 합니다 — 처음 만난 서버와 어떻게? 비대칭키(공개키/개인키)는 이 문제를 풀지만 느립니다. TLS의 전략: 비대칭키로 “대칭키를 안전하게 합의” 하고, 이후 실제 데이터는 빠른 대칭키로 암호화합니다. 두 세계의 장점만 취합니다.

현대(TLS 1.3)는 합의 방식으로 (EC)DHE — 키 교환마다 임시 키쌍을 쓰는 방식을 표준으로 합니다. 덕분에 forward secrecy(나중에 서버 개인키가 유출돼도, 과거에 녹음해 둔 트래픽은 복호화 불가)가 보장됩니다.

핸드셰이크를 움직임으로 보기 (TLS 1.3, 1-RTT)

ClientHello로 시작해, 단 한 번의 왕복(1-RTT) 만에 양쪽이 같은 세션 키를 갖고 암호화된 통신을 시작합니다. 파랑은 클라이언트→서버, 초록은 서버→클라이언트입니다.

Client Server ① ClientHello (지원 암호군 · 키 공유 · SNI · ALPN) ② ServerHello + Certificate + 키 + Finished ③ (인증서 검증) Finished 🔒 암호화

TLS 1.2는 같은 일을 2-RTT로 했습니다(ServerHello 후 키 교환에 한 번 더 왕복). 1.3은 ClientHello에 미리 키 공유 후보를 실어 1-RTT로 줄였고, 재방문 시에는 이전 세션 정보를 활용해 0-RTT(첫 패킷에 데이터까지)도 가능합니다 — QUIC/HTTP3의 빠른 연결이 이 위에 있습니다.

인증: “이 공개키가 진짜 그 서버 것”을 누가 보증하나

암호화만으로는 중간자(MITM) 를 못 막습니다 — 공격자와 안전하게 암호화하는 건 의미가 없으니까요. 그래서 서버의 공개키가 진짜 그 도메인 소유자 것임을 제3자(CA, 인증 기관)가 보증합니다. 이게 인증서입니다.

신뢰는 체인으로 이어집니다. 브라우저/OS에는 소수의 루트 CA가 미리 박혀 있고(트러스트 스토어), 루트가 중간 CA를 서명하고, 중간 CA가 서버 인증서를 서명합니다. 클라이언트는 서버 인증서 → 중간 → 루트까지 서명을 거슬러 검증해, 루트에 닿으면 신뢰합니다.

서버 인증서 example.com 중간 CA 서버 인증서 서명 루트 CA 트러스트 스토어 서명 검증 ✓ 서명 검증 ✓ 루트 도달 ✓ 신뢰

현장 1위 사고: “중간 인증서 누락”. 서버가 자기 인증서만 내려보내고 중간 CA를 빠뜨리면, 중간 CA를 이미 캐시한 브라우저는 통과하지만 그렇지 않은 클라이언트(일부 모바일·서버 간 호출)는 체인을 못 이어 실패합니다. “어떤 기기에서만 SSL 오류”의 전형입니다. 서버는 풀 체인(서버+중간) 을 함께 제공해야 합니다.

핸드셰이크에 끼어드는 두 협상: SNI와 ALPN

한 IP/포트에 여러 도메인을 호스팅할 때(가상 호스팅·CDN), 서버는 “어느 도메인의 인증서를 줄지” 알아야 합니다. 그런데 그건 암호화 전에 정해야 하죠. 그래서 ClientHello가 SNI(Server Name Indication) 에 목적지 도메인을 (1.2에선 평문으로) 담습니다 — SNI가 틀리면 엉뚱한 인증서가 와서 실패합니다. 또 ClientHello의 ALPN으로 h2/http/1.1을 협상해 HTTP 버전을 정합니다.

mTLS: 서버뿐 아니라 클라이언트도 증명한다

기본 TLS는 서버만 인증합니다(클라이언트는 익명). mTLS(상호 TLS) 는 클라이언트도 인증서를 제시해 서로를 검증합니다. 사람이 아니라 서비스 간 통신에서 “이 호출자가 진짜 우리 서비스인지”를 보증하는 핵심 수단이라, 서비스 메시·zero trust의 토대가 됩니다.

디버깅

1
2
3
4
5
6
7
8
# 핸드셰이크·인증서 체인·협상된 버전/암호군을 직접 본다
openssl s_client -connect example.com:443 -servername example.com </dev/null

# 인증서 만료일·체인만 빠르게
echo | openssl s_client -connect example.com:443 2>/dev/null | openssl x509 -noout -dates -issuer -subject

# ALPN으로 h2가 협상되는지
openssl s_client -alpn h2 -connect example.com:443 </dev/null | grep -i alpn

s_client는 “체인이 끝까지 이어지나(Verify return code: 0 (ok))”, “어떤 TLS 버전·암호군이 잡혔나”, “SNI를 줬을 때 올바른 인증서가 오나”를 한눈에 보여줘 만료·체인·SNI 세 사고를 가릅니다.

면접/리뷰 단골 질문

  • Q. 비대칭키가 있는데 왜 대칭키로 바꾸나? → 비대칭은 느려서 데이터 암호화엔 부적합. 비대칭으로 대칭키를 합의만 하고, 데이터는 빠른 대칭키(AES)로 처리.
  • Q. forward secrecy가 뭐고 왜 중요? → 매 세션 임시 키((EC)DHE)를 써서, 서버 개인키가 나중에 유출돼도 과거 트래픽은 복호화 불가. TLS 1.3은 이를 사실상 강제.
  • Q. 일부 클라이언트에서만 SSL 오류가 난다. 첫 의심은? → 중간 인증서 누락(풀 체인 미제공). openssl s_client로 Verify return code 확인.
  • Q. TLS 1.3이 1.2보다 빠른 이유는? → 핸드셰이크를 2-RTT→1-RTT로 줄이고, 재방문 시 0-RTT 지원 + 취약한 레거시 암호군 제거.

정리

  • TLS = TCP 위, HTTP 아래에서 기밀성·무결성·인증을 더하는 층. HTTPS = HTTP+TLS.
  • 비대칭으로 대칭키 합의 → 대칭키로 데이터 처리. 1.3은 (EC)DHE로 forward secrecy를 기본화.
  • 인증은 인증서 체인(서버→중간→루트 CA)으로 신뢰를 형성 — 중간 인증서 누락이 최다 사고.
  • 핸드셰이크에 SNI(어느 도메인) + ALPN(어느 HTTP 버전)이 함께 협상되고, mTLS는 서비스 간 신뢰의 토대.

다음 글: 이제 패킷이 사설망과 공인망을 넘나드는 NAT와 사설망으로 이어집니다.

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.

시리즈 · 네트워크 A-Z (20편)

  1. 네트워크란 무엇인가 — 회선 교환 vs 패킷 교환, 왜 인터넷은 패킷을 선택했나
  2. OSI 7계층과 TCP/IP — 왜 네트워크를 굳이 층으로 쪼갰나
  3. 이더넷·MAC·스위치 — 스위치는 어떻게 "어느 포트로 보낼지" 스스로 배우나
  4. IP 주소와 서브넷팅 — /24를 /26으로 쪼갠다는 게 비트 단위로 무슨 뜻인가
  5. 라우팅은 어떻게 길을 찾나 — 라우팅 테이블, Longest Prefix Match, 그리고 BGP
  6. ARP와 L2/L3 경계 — IP는 아는데 왜 MAC을 또 묻나
  7. TCP는 어떻게 신뢰를 만드나 — 3-way handshake, 상태 머신, 그리고 TIME_WAIT
  8. TCP는 얼마나 빨리 보낼까 — 흐름 제어 vs 혼잡 제어, 그리고 톱니파의 비밀
  9. UDP와 QUIC — TCP를 버려야 더 빨라지는 순간
  10. DNS는 어떻게 이름을 주소로 바꾸나 — 루트부터 권한 서버까지, 그리고 캐시의 마법
  11. HTTP의 진화 — 1.1의 줄 서기에서 HTTP/2 멀티플렉싱, HTTP/3까지
  12. TLS/HTTPS는 어떻게 도청 불가능한 채널을 만드나 — 핸드셰이크부터 인증서 체인까지 지금 보는 글
  13. NAT와 사설망 — 주소 하나로 수백 대가 인터넷을 쓰는 법, 그리고 P2P가 막히는 이유
  14. 로드 밸런싱 — L4와 L7은 무엇이 다르며, 왜 헬스체크가 서비스를 살리고 죽이나
  15. 프록시·리버스 프록시·CDN — 콘텐츠를 사용자 코앞으로 옮기는 기술
  16. 방화벽·보안그룹·네트워크 ACL — stateful과 stateless가 가르는 모든 것
  17. VPC & 클라우드 네트워킹 — 라우팅 테이블 한 줄이 퍼블릭과 프라이빗을 가른다
  18. 소켓과 커널 네트워크 스택 — accept 한 줄 뒤에서 벌어지는 일
  19. 네트워크 성능 — 대역폭은 왜 빠름이 아닌가 (RTT·BDP·처리량의 진실)
  20. 현대 네트워킹 — 사이드카, eBPF, 그리고 경계가 사라진 신뢰