대부분 HTTPS가 어디로 가는지 숨겨준다고 생각한다.

아니다. 완전히는.

페이지 콘텐츠는 암호화된다. 쿠키도. 요청 경로도. 하지만 그 모든 보호가 시작되기 전에, 클라이언트가 서버에게 어떤 호스트명을 원하는지 알려줘야 한다. 그 호스트명은 TLS ClientHello 안에 Server Name Indication — SNI — 으로 평문 전송된다. 네트워크 경로에 있는 누구든 어떤 사이트에 접속하는지 볼 수 있다.

HTTPS가 바람에 내놓고 간 부분이다.

SNI가 존재하는 이유

SNI는 아주 평범한 호스팅 문제를 풀었다.

웹이 가상 호스팅으로 넘어가면서 하나의 IP 주소가 여러 HTTPS 사이트를 서빙하게 됐다. 핸드셰이크가 진행되기 전에 서버가 어떤 인증서를 제시할지 알아야 한다. 10개 도메인이 하나의 엣지 IP를 공유하면 클라이언트가 a.example을 원하는지 b.example을 원하는지 모르면 올바른 인증서 체인을 고를 수 없다.

그래서 TLS가 ClientHello에 확장을 추가했다: “이 호스트명 때문에 왔습니다.”

단순하다. 필요하다. 그리고 완전히 암호화되지 않았다.

ClientHello는 TLS 핸드셰이크의 첫 번째 메시지다. 클라이언트와 서버가 아무 키도 공유하기 전에 보내지므로, 그 안의 내용은 평소 방식으로 암호화할 수가 없다. 안의 SNI 필드는 ISP, 기업 프록시, 국가 방화벽, 카페 라우터 — 보는 누구에게나 읽힌다.

뭐가 유출되나

HTTPS는 통신 내용을 보호한다. SNI는 목적지를 유출한다.

ISP가 사이트에서 어떤 페이지를 보는지는 못 본다. 하지만 방문하는 모든 사이트의 완전한 목록을 만들 수 있다. 프로파일링, 표적 광고, 감시에 충분하다. 일부 국가에서는 검열에 충분하다.

검열과 필터링 시스템이 작동하는 방식의 큰 부분이 이것이다. 중국의 만리방화벽, 기업 콘텐츠 필터, ISP 수준 자녀 보호 기능은 IP 주소, DNS와 함께 SNI 필드를 검사해서 차단 여부를 정한다. 암호화를 깰 필요가 없다. 호스트명이 바로 거기, 첫 패킷의 평문에 있다.

DNS-over-HTTPS로 DNS 쿼리를 숨겨도 SNI 필드가 목적지를 드러낸다. 질문(“example.com의 IP가 뭐야?”)은 암호화했는데 바로 다음 패킷에서 답을 발표한 셈이다.

TLS 1.2는 더 샜다. 서버 인증서가, 거기 담긴 호스트명 전부와 함께 평문으로 오갔다. TLS 1.3은 인증서를 암호화했고, 그 결과 눈에 띄는 누출은 SNI 하나만 남았다. 접속하는 IP 주소는 어느 쪽이든 보인다. 여러 사이트가 공유하는 CDN 주소라면 별 의미가 없지만, 전용 서버라면 그것만으로 다 드러날 수 있다.

도메인 프론팅: 죽은 꼼수

한동안 영리한 우회 방법이 있었다.

도메인 프론팅은 SNI와 HTTP 사이의 격차를 악용했다. SNI에는 검열자가 차단하지 않을 인기 CDN 도메인을 넣고, HTTP Host 헤더에는 실제 목적지를 넣는다(암호화 터널 안에 숨겨진). 검열자는 SNI를 보고 통과시킨다. CDN은 Host 헤더를 읽고 진짜 사이트로 라우팅한다.

Signal은 2016년부터 이 방법으로 이집트, UAE, 오만, 카타르, 이란의 차단을 우회했다. Tor의 meek 같은 다른 우회 도구도 썼다.

그다음 클라우드 제공자가 죽였다. 2018년 4월 구글은 원래 지원하는 기능이 아니었다며 인프라를 바꿔 프론팅을 막았고, Amazon도 같은 달 CloudFront를 바꾸면서 Signal에 프론팅이 약관 위반이라고 통보했다. Microsoft는 2021년 Azure에서도 허용하지 않겠다고 발표했다. 남의 인프라로 트래픽을 위장한 것이니 일면 이해는 간다. 하지만 반체제 인사들이 의존하던 도구도 함께 사라졌다.

Encrypted Client Hello: 진짜 해결책

제대로 된 해결책은 Encrypted Client Hello, ECH다. ESNI라는 초기 초안에서 출발해 2026년 3월 RFC 9849로 마침내 발행됐다.

아이디어: SNI 필드 자체를 암호화한다. 클라이언트가 DNS에서 공개키를 가져오고(HTTPS 또는 SVCB 레코드에 게시된), 그걸로 실제 호스트명이 담긴 내부 ClientHello를 암호화하고, 일반적 커버 이름만 보여주는 외부 ClientHello로 감싼다.

관찰자가 보는 것: cloudflare-ech.com(또는 커버가 뭐든)에 연결. 실제 목적지는 암호화됐다. 개인키를 가진 서버만 읽을 수 있다.

ECH가 이렇게 오래 걸리는 이유

ECH는 잘 협조하지 않는 세 레이어의 협조가 필요하다:

DNS 인프라. 서버의 ECH 공개키가 DNS에 HTTPS/SVCB 레코드로 게시돼야 한다. 모든 DNS 제공자가 이 레코드 타입을 지원하지는 않는다.

CDN 지원. ECH는 큰 사업자가 여러 사이트의 TLS를 커버 이름 하나 뒤에서 종단할 때 가장 잘 작동한다. 그 군중 속에 숨는 게 핵심이기 때문이다. Cloudflare는 2023년 모든 요금제에서 ECH를 켤 수 있게 했다. 그 밖에서는 채택이 느리고, 자기 IP 주소를 따로 쓰는 사이트는 주소가 이미 정체를 드러내니 얻는 게 별로 없다.

브라우저 배포. 이 층은 사실상 끝났다. 2023년 말 Chrome은 117 버전, Firefox는 119 버전에서 ECH를 기본으로 켰다(Firefox는 DNS-over-HTTPS를 켰을 때만 쓴다). 하지만 서버가 키를 게시하지 않으면 브라우저도 ECH를 쓸 수 없다.

미들박스 문제. 기업 프록시와 방화벽이 콘텐츠 필터링을 위해 SNI를 검사한다. ECH가 그걸 깨뜨린다. 기업 IT는 가시성을 잃느니 ECH 연결을 완전히 차단할 것이다.

현재 상태

2026년에도 SNI는 대부분의 연결에서 목적지를 드러낸다. 브라우저는 준비됐고, 서버는 대부분 아니다. Cloudflare 밖에서 ECH 배포는 드물다. 보편적 배포는 일어난다 해도 몇 년 뒤다.

내 연결이 새는지 직접 보고 싶다면 Wireshark로 TLS 핸드셰이크를 잡아 ClientHello의 server_name 확장을 보면 된다. 아니면 https://crypto.cloudflare.com/cdn-cgi/trace에 접속해 sni=encrypted(ECH 동작)인지 sni=plaintext인지 확인하면 된다.

편지를 암호화했는데 봉투 겉에 주소를 써놓았다. 20년 후에도 봉투까지 암호화할지 아직 논쟁 중이다.