HTTPS는 별도의 프로토콜이 아니라 HTTP를 TLS(Transport Layer Security) 위에서 실어 나르는 것입니다. 브라우저와 서버가 먼저 TLS 세션을 수립해 암호화된 터널을 만든 뒤, 그 안으로 평소의 HTTP 요청·응답을 주고받습니다. 그래서 https://로 접속하면 URL·헤더·본문 전체가 네트워크 위에서 암호화되어, 중간의 공격자가 내용을 읽거나 변조할 수 없습니다.
TLS가 보장하는 세 가지
TLS는 세 가지 보안 속성을 동시에 제공합니다. 기밀성(confidentiality)은 대칭키 암호로 통신 내용을 숨기고, 무결성(integrity)은 AEAD(예: AES-GCM, ChaCha20-Poly1305) 또는 MAC으로 전송 중 변조를 탐지하며, 인증(authentication)은 인증서로 상대 서버가 진짜 그 도메인의 주인임을 증명합니다. 기밀성만 있고 인증이 없으면, 암호화는 되지만 '누구와' 안전하게 통신하는지 알 수 없어 중간자 공격에 그대로 노출됩니다.
핸드셰이크: TLS 1.2 전체 흐름
TLS 1.2의 전형적인 핸드셰이크는 여러 왕복(round trip)으로 이뤄집니다. 클라이언트가 ClientHello로 지원하는 TLS 버전·cipher suite 목록·랜덤 값을 보내면, 서버는 ServerHello로 선택한 파라미터와 자신의 인증서(Certificate)를 응답합니다. 이어 키 교환 파라미터를 교환하고, 양쪽이 같은 대칭 세션키를 도출한 뒤 Finished 메시지로 핸드셰이크를 확정합니다.
Client Server
| ClientHello (versions, ciphers, rand) |
| ---------------------------------------> |
| ServerHello (chosen cipher, rand) |
| Certificate (server cert chain) |
| ServerKeyExchange (ECDHE params) |
| ServerHelloDone |
| <--------------------------------------- |
| ClientKeyExchange (ECDHE pubkey) |
| ChangeCipherSpec / Finished |
| ---------------------------------------> |
| ChangeCipherSpec / Finished |
| <--------------------------------------- |
| ==== encrypted application data ==== |
여기서 핵심은 키 교환(key exchange)입니다. 현대 배포는 거의 예외 없이 ECDHE(타원곡선 Diffie-Hellman, ephemeral)를 씁니다. 매 세션마다 임시 키쌍을 새로 만들어 세션키를 도출하므로, 훗날 서버의 개인키가 유출되더라도 과거에 녹화된 트래픽을 복호화할 수 없습니다. 이 성질을 순방향 비밀성(forward secrecy)이라고 하며, 오래된 RSA 키 교환에는 없는 중요한 이점입니다.
TLS 1.2 vs TLS 1.3
TLS 1.3(RFC 8446)은 핸드셰이크를 대폭 단순화·고속화하고 취약한 옛 알고리즘을 걷어냈습니다.
- 속도: 1.2는 보통 2-RTT가 필요하지만, 1.3은 1-RTT로 줄었고 재접속 시 0-RTT로 애플리케이션 데이터를 첫 왕복에 실어 보낼 수 있습니다.
- 안전한 기본값: 1.3은 순방향 비밀성이 없는 정적 RSA 키 교환과 취약한 cipher(RC4, 3DES, CBC 모드 등)를 아예 제거했습니다. 남은 cipher suite는 모두 AEAD입니다.
- 암호화 범위 확대: 1.3은
ServerHello이후의 인증서 등 핸드셰이크 메시지 상당수를 암호화해 관찰자에게 덜 노출합니다. - 0-RTT의 함정: 0-RTT로 보낸 초기 데이터는 재전송 공격(replay)에 취약할 수 있어,
GET같은 멱등·안전한 요청에만 쓰도록 주의해야 합니다.
인증서와 CA
서버 인증서는 '이 공개키는 이 도메인의 것'임을 인증 기관(CA, Certificate Authority)이 서명으로 보증한 문서입니다. 브라우저는 서버가 보낸 인증서 체인을 따라 올라가 자신이 신뢰하는 루트 CA에 도달하는지, 도메인 이름(및 SAN)이 일치하는지, 유효 기간이 지나지 않았는지, 폐기되지 않았는지를 검증합니다. Let's Encrypt 같은 무료 CA와 ACME 자동화 덕분에 오늘날 인증서 발급·갱신은 대부분 자동으로 이뤄집니다.
SNI와 ALPN
하나의 IP가 여러 도메인을 호스팅하는 시대이므로, 서버는 클라이언트가 어느 도메인의 인증서를 원하는지 알아야 합니다. SNI(Server Name Indication)는 ClientHello에 목표 호스트명을 실어 이 문제를 해결합니다. 다만 SNI는 평문이라 관찰자에게 호스트명이 노출되는데, 이를 가리기 위한 후속 표준이 ECH(Encrypted Client Hello)입니다. 한편 ALPN(Application-Layer Protocol Negotiation)은 같은 핸드셰이크 안에서 앞으로 쓸 애플리케이션 프로토콜(h2, http/1.1 등)을 미리 합의해, 별도 왕복 없이 HTTP/2로 바로 진입하게 해 줍니다.
Cipher suite와 혼합 콘텐츠
cipher suite는 키 교환·인증·대칭 암호·해시 알고리즘의 묶음입니다(예: TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256). 서버는 안전한 suite만 우선순위로 제공하도록 구성해야 합니다. 마지막으로 흔한 실전 문제가 혼합 콘텐츠(mixed content)입니다. HTTPS 페이지가 이미지·스크립트를 http://로 불러오면 브라우저가 이를 차단하거나 경고합니다. 특히 스크립트 같은 active 혼합 콘텐츠는 페이지 전체의 보안을 무너뜨릴 수 있으므로 반드시 차단됩니다. 모든 서브리소스를 https://(또는 프로토콜 상대 경로 대신 명시적 https)로 통일하는 것이 정답입니다.