SNI를 암호화하면 통제를 벗어날까 — 은닉이 끝나는 곳, CDN

"오리진 IP를 숨기고 SNI까지 암호화하면 그 트래픽은 아무도 통제할 수 없다." 인프라 설계 논의에서 가끔 듣는 말이다. 절반만 맞다. 이 명제는 "누가, 어디서 관찰하는가"라는 질문을 건너뛴다. 은닉 기술이 상대하는 것은 경로 위의 관찰자다. 요청을 받아 복호화하고 응답을 결정하는 종단은 처음부터 상대가 아니다. 그리고 현대 웹에서 그 종단은 대부분 CDN이다.
SNI는 ClientHello 어디에 있고 무엇을 노출하나
답부터 말하면 SNI(Server Name Indication)는 TLS 핸드셰이크의 첫 메시지인 ClientHello 안 server_name 확장에 들어간다.1 CDN처럼 하나의 IP가 수천 개 도메인을 서빙하는 구조에서, 서버는 이 값을 보고 올바른 인증서를 고른다. 문제는 전통적 TLS에서 이 필드가 평문이라는 점이다. 페이로드는 암호화돼도 목적지 도메인은 경로상의 네트워크 장비 누구나 읽을 수 있다. 기업 방화벽의 필터링과 통신사 장비의 정책 적용이 모두 이 지점에서 작동해 왔다.
ECH가 성립하는 두 가지 조건
ECH(Encrypted Client Hello)는 실제 SNI를 포함한 ClientHelloInner를 암호화해 ClientHelloOuter 안에 싣는 TLS 1.3 확장이다. ESNI의 후속으로 2026년 3월 RFC 9849로 표준이 확정됐다.2
ECH가 실제로 동작하려면 두 요소가 모두 갖춰져야 한다.
- DNS 게시: 권위 DNS가 HTTPS(type65) 레코드의
ech=SvcParam에 하나 이상의 ECHConfig를 묶은 ECHConfigList를 게시한다. - 클라이언트 암호화와 종단 복호화: 클라이언트가 목록에서 고른 ECHConfig의 공개키로 ClientHelloInner를 암호화해 Outer의 ECH 확장에 싣고, 엣지(CDN)가 대응하는 개인키로 Inner를 복호화한다.
DNS에 ECH 설정이 없으면 클라이언트는 ECH를 제안할 재료가 없어 일반 ClientHello를 보낸다. 반대로 설정을 받았는데 종단이 처리하지 못하면 재시도 절차를 거치거나 핸드셰이크가 실패할 수 있다. 인증된 실패 뒤에 무조건 평문으로 낮추는 단순 폴백은 아니다.2 여기서 자주 헷갈리는 지점이 있다. Route 53 같은 DNS 서비스에 ech=가 담긴 HTTPS 레코드를 올리는 것은 (1)일 뿐이다. CloudFront 엣지가 (2)를 수행하지 않으면 ECH는 성립하지 않는다. 레코드 호스팅과 엣지 복호화는 별개의 기능이다.

제어는 어느 계층에서 일어나는가
콘텐츠 제어는 기업 필터링이든 지역 정책이든 법적 준수든 하나의 지점에서 일어나지 않는다. 계층마다 관찰 대상과 은닉 수단이 다르다.
| 계층 | 위치 | 관찰 대상 | 암호화로 은닉되나 |
|---|---|---|---|
| DNS 전송 | 경로 관찰자 | 암호화되지 않은 도메인 질의 | DoH/DoT로 가능 |
| DNS 해석 | 재귀 리졸버 | 클라이언트가 질의한 이름 | 불가. 리졸버가 DoH/DoT를 종료한다 |
| SNI 검사 | 경로 중간 장비 | 평문 SNI | ECH로 가능 |
| CDN/애플리케이션 | 목적지 엣지 | 요청 전체 | 불가. 복호화의 주체가 종단이다 |
마지막 행이 이 글의 결론을 담고 있다. ECH를 복호화하는 열쇠는 CDN이 쥔다. ECH가 경로 관찰자에게 숨긴 ClientHello 정보는 그 CDN에 가려지지 않는다. 은닉 기술은 관찰자의 수를 줄이는 기술이지, 종단의 결정을 바꾸는 기술이 아니다.
국내 DNS·IP·SNI 방식과 해외 과차단 사례를 계층별로 비교하려면 사이트 차단은 어디서 일어날까에서 이어서 볼 수 있다.
451과 연결 리셋은 다른 말을 한다
계층 모델은 장애 진단에서 바로 힘을 낸다. 연결 실패는 계층마다 지문이 다르다.
- DNS 해석 단계: 이름 해석 자체가 실패한다. NXDOMAIN 같은 리졸버 응답이 남는다.
- TLS 핸드셰이크 단계: ClientHello 뒤에 연결이 끊기고 HTTP 상태코드는 없다. 이 지문만으로 어느 장비가 끊었는지는 알 수 없다.
- HTTP 응답 단계: TLS 뒤에
451 Unavailable For Legal Reasons3나403같은 응답이 돌아온다. 응답 단계까지 진행됐다는 뜻이며,Server헤더·응답 본문·운영 로그를 함께 봐야 CDN·프록시·오리진 중 응답 주체를 좁힐 수 있다.

핵심은 이것이다. HTTP 상태코드는 연결이 HTTP 응답 단계까지 진행됐다는 증거다. ClientHello 직후 끊긴 연결과는 구분할 수 있다. 다만 451 하나만으로 CDN, 중간 프록시, 오리진 중 누가 응답했는지는 확정할 수 없다. 이 사례처럼 응답 표식과 운영 로그가 맞아야 행위자를 특정할 수 있다.
운영자 쪽에는 통제가 기록된다
이 사례에서는 방문자에게 보인 451과 같은 사건이 존 운영 계정에도 남았다. Cloudflare는 계정 대시보드의 Abuse Reports에 신고 내역과 완화 조치를 기록했다. 화면에서 그 구조가 드러난다.

두 가지가 눈에 띈다. 첫째, 이 사례의 조치는 Geo block이다. CDN은 요청의 출발지 정보를 바탕으로 특정 관할 트래픽에 정책을 적용할 수 있다. 다만 관할별 적용만으로 행위자가 CDN이라고 단정할 수는 없다. 경로 사업자도 출발지 네트워크나 위치 신호를 쓸 수 있다. 여기서는 Cloudflare 응답 페이지와 운영자 대시보드 기록이 함께 있어 CDN 측 조치로 좁힐 수 있다. 둘째, 근거가 문서로 남는다. 접수 날짜, 조치 상태, 대상 엔티티가 리포트 단위로 관리되고, 운영자는 Trust & Safety 리뷰 요청이라는 이의 절차로 연결된다.

증거 항목에는 규제기관이 지정한 위법 카테고리가 그대로 인용되고, "법이 이 명령에서 벗어날 재량을 주지 않는다"는 문장까지 기록된다. 이 기록에 한해서는 법적 명령이 조치 근거로 제시됐고, 범위도 운영자 관리 화면에 문서화됐다. 모든 CDN 차단의 근거를 일반화할 수는 없지만, 이 사례는 통제점이 어떻게 기록되는지 보여준다.
CDN마다 다른 ECH
같은 "CDN 뒤 호스팅"이라도 SNI 노출 특성은 CDN 선택에 따라 갈린다. 2026년 7월 기준 현황이다.
- Cloudflare: ECH를 지원하며 Free 존은 기본 활성화되고 다른 플랜은 Edge Certificates에서 켤 수 있다.4 HTTPS 레코드에 ECH 설정을 게시하고 엣지에서 Inner ClientHello를 복호화한다. 두 조건을 모두 채운 상태다.
- AWS CloudFront: 2026년 7월 16일 확인한 뷰어 프로토콜·암호군 문서에는 ECH 지원이 명시돼 있지 않다.5 Route 53이
ech=레코드를 호스팅할 수 있다는 사실만으로 CloudFront 엣지 복호화를 추론하면 안 된다. - 서버 진영: NGINX 1.29.4는 ECH 지원을 실었고 공개 당시 OpenSSL의 ECH feature branch를 요구했다.6 OpenSSL 4.0 정식판도 RFC 9849 ECH를 포함한다.7 다만 두 발표만으로 특정 NGINX·OpenSSL 조합의 빌드 호환성이 보장되지는 않는다. 실제 도입 전 빌드 옵션, 링크 호환성, 핸드셰이크 상호운용을 검증해야 한다.
SNI·ECH 자주 묻는 질문
SNI 암호화란 무엇인가
SNI 암호화에서 클라이언트는 ClientHelloInner에 실제 목적지 이름을 넣고, ECHConfigList에서 고른 ECHConfig의 공개키로 인코딩된 Inner를 암호화한다. 암호문은 공용 이름을 담은 ClientHelloOuter의 ECH 확장에 실리고, 대응하는 개인키를 가진 종단이 Inner를 복원한다.2 HTTP 요청과 응답을 처리하는 CDN까지 목적지를 모르게 만드는 기술은 아니다.
SNI는 ClientHello 어디에 나타나는가
ECH를 쓰지 않으면 실제 도메인이 ClientHello의 server_name 확장에 평문으로 나타난다. ECH를 쓰면 실제 도메인은 암호화된 Inner의 server_name에 있고, Outer에는 ECH 설정의 public_name이 들어간다. 따라서 패킷에서 Outer SNI만 봤다고 실제 방문 도메인을 알아낸 것은 아니다.
cloudflare-ech.com은 무엇인가
cloudflare-ech.com은 Cloudflare가 ECH 트래픽의 ClientHelloOuter에 사용하는 공용 SNI다.4 사용자가 그 사이트를 방문했다는 뜻이 아니다. 실제 도메인은 암호화된 ClientHelloInner에 있으며 Cloudflare 엣지만 읽는다. 여러 ECH 지원 사이트가 같은 바깥 이름으로 보이는 것이 은닉 집합을 만든다.
ECH가 실제로 동작하는지 어떻게 확인하나
DNS 레코드 하나만으로는 충분하지 않다. 다음 순서로 ECH 검증 체크리스트를 통과해야 한다.
dig <도메인> type65 +short로 HTTPS 레코드의ech=SvcParam에 ECHConfigList가 게시됐는지 확인한다.- 최신 브라우저를 사용한다. Firefox는 119부터 ECH가 기본 활성화됐지만 기업 정책, 투명 프록시나 가족 보호 설정은 ECH를 끌 수 있다.8
- DoH나 DoT 같은 암호화 DNS도 함께 사용한다. ECH가 TLS의 실제 SNI를 숨겨도 평문 DNS 질의가 목적지를 다시 드러낼 수 있기 때문이다.
- Mozilla가 안내하는 Cloudflare Browser Security Check에서 클라이언트의 ECH 사용 여부를 확인한다.8
- 필요하면 패킷 캡처에서 실제 도메인 대신 공급자의 public name이 Outer SNI에 보이는지 확인한다.
- 원래 도메인의 인증서와 HTTP 응답까지 정상인지 확인한다. DNS 게시, 브라우저 사용, 종단 복호화 세 단계가 모두 성공해야 한다.
계층을 특정하는 진단
운영 중 만나는 연결 문제를 계층으로 분류하는, 재현 가능한 진단 절차다.
ECH 게시 확인. 도메인의 HTTPS 레코드에 ech=가 있는지 본다.
dig example.com type65 +short
결과는 세 갈래다. ECH를 게시한 도메인은 HTTPS 레코드 안에 ech SvcParam을 포함한다. ECH 없이 alpn이나 IP 힌트만 게시할 수도 있고, HTTPS 레코드 자체가 없으면 빈 응답이다. 따라서 응답이 있다는 사실보다 ech 설정의 포함 여부를 봐야 한다. 아래 예시는 구형 dig가 전체 HTTPS 레코드를 바이너리 블롭으로 출력한 경우다.
$ dig example-a.org type65 +short
\# 136 000100000100060268330268320004000868152384AC43DE92000500
470045FE0D004165002000206D6ED8B048EDBCDA3DBCDE74FC3CF52E
D8C6DB3F991F733282F117CDA36F8A6B0004000100010012636C6F75
64666C6172652D6563682E636F6D0000000600202606470030360000
0000000068152384260647003037000000000000AC43DE92
$ dig example-b.org type65 +short
$
블롭 안에 ech= ECHConfigList가 들어 있다. 셋째 줄 끝에서 넷째 줄로 이어지는 636C6F75 64666C6172652D6563682E636F6D 구간을 ASCII로 풀면 cloudflare-ech.com, Outer ClientHello에 실릴 public name이다. ECH의 조건 (1), 곧 ECHConfigList의 DNS 게시가 충족돼 있다는 증거다.
함정이 하나 있다. 일부 dig 버전은 dig -t HTTPS나 dig https를 "invalid type"으로 무시하고 A 레코드 조회로 폴백한다. 숫자형 type65가 안전하다. 리졸버가 HTTPS 레코드를 스트립하는지 의심되면 로컬 리졸버와 공개 리졸버(@1.1.1.1, @8.8.8.8)를 교차 비교한다.
끊기는 계층 확인. 같은 IP에 SNI 값만 바꿔 핸드셰이크를 비교한다.
openssl s_client -connect <IP>:443 -servername example.com
특정 SNI에서만 ClientHello 직후 연결이 끊기고 다른 SNI는 핸드셰이크 응답을 받는다면, 결과가 SNI 값에 의존한다는 뜻이다. 이 비교만으로 경로 장비, CDN 엣지, 가상 호스트 설정 중 어느 주체가 차이를 만들었는지는 알 수 없다. 두 경우의 출력은 첫 줄부터 다르다.
ClientHello 직후 끊긴 연결의 전체 출력
$ openssl s_client -connect <edge-ip>:443 -servername example-a.org
Connecting to <edge-ip>
CONNECTED(00000003)
error:0A000126:SSL routines::unexpected eof while reading:ssl/record/rec_layer_s3.c:703:
---
no peer certificate available
---
No client certificate CA names sent
Negotiated TLS1.3 group: <NULL>
---
SSL handshake has read 0 bytes and written 1552 bytes
Verification: OK
---
New, (NONE), Cipher is (NONE)
Protocol: TLSv1.3
This TLS version forbids renegotiation.
Compression: NONE
Expansion: NONE
No ALPN negotiated
Early data was not sent
Verify return code: 0 (ok)
---
error:0A000197:SSL routines:SSL_shutdown:shutdown while in init:ssl/ssl_lib.c:2804:
핵심은 세 줄이다. unexpected eof while reading, no peer certificate available, 그리고 read 0 bytes. ClientHello 1552바이트를 써 보냈는데 한 바이트도 돌아오기 전에 연결이 닫혔다. 인증서도, 암호군 협상도, HTTP 상태코드도 없다.
같은 명령이 서버까지 도달하면 첫 화면부터 인증서 체인이 쏟아진다.
서버까지 완주한 핸드셰이크의 전체 출력
$ openssl s_client -connect <edge-ip>:443 -servername example-b.com
Connecting to <edge-ip>
CONNECTED(00000003)
depth=3 C=US, O=Internet Security Research Group, CN=ISRG Root X2
verify return:1
depth=2 C=US, O=ISRG, CN=Root YE
verify return:1
depth=1 C=US, O=Let's Encrypt, CN=YE1
verify return:1
depth=0 CN=example-b.com
verify return:1
---
Certificate chain
0 s:CN=example-b.com
i:C=US, O=Let's Encrypt, CN=YE1
a:PKEY: EC, (prime256v1); sigalg: ecdsa-with-SHA384
v:NotBefore: Jun 20 00:37:09 2026 GMT; NotAfter: Sep 18 00:37:08 2026 GMT
1 s:C=US, O=Let's Encrypt, CN=YE1
i:C=US, O=ISRG, CN=Root YE
...
---
Server certificate
-----BEGIN CERTIFICATE-----
(생략)
-----END CERTIFICATE-----
subject=CN=example-b.com
issuer=C=US, O=Let's Encrypt, CN=YE1
---
Peer signing digest: SHA256
Peer signature type: ecdsa_secp256r1_sha256
Negotiated TLS1.3 group: X25519MLKEM768
---
SSL handshake has read 4963 bytes and written 1620 bytes
Verification: OK
---
New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384
Protocol: TLSv1.3
Verify return code: 0 (ok)
---
read 4963 bytes, 협상된 암호군 TLS_AES_256_GCM_SHA384, Verify return code: 0. TLS 상대가 핸드셰이크 응답을 반환한 지문이다. 앞의 출력과 나란히 놓으면 실패가 핸드셰이크 응답 전인지 후인지를 분류하고 SNI 의존성을 확인할 수 있다. 행위자 특정에는 패킷 경로, 응답 표식과 운영 로그가 더 필요하다.
판단
아키텍처 관점의 테이크어웨이는 네 가지다.
- 은닉은 통제 불가와 같은 말이 아니다. 오리진 IP 은닉과 SNI 은닉(ECH)은 제3자 관찰자를 상대한다. 목적지 CDN이 정책적으로 거부하기로 하면 두 기술은 그 결정을 바꾸지 못한다.
- CDN은 중요한 통제점이 된다. 트래픽을 CDN 뒤로 모을수록 관찰·차단·컴플라이언스 기능이 CDN에 많이 모인다. 오리진 보호라는 이점과 함께 거버넌스·가용성 의존도도 커진다.
- 단일 지표는 단계를 분류할 뿐 행위자를 특정하지 않는다. 상태코드 유무는 HTTP 응답 전후를, 같은 IP의 SNI별 차이는 SNI 의존성을 보여준다. CDN·경로 장비·오리진 중 누가 결정했는지는 추가 증거로 확인해야 한다.
- ECH는 CDN 선택의 숨은 변수다. HTTPS 레코드 게시와 엣지 복호화 지원 여부가 CDN마다 달라, 서비스의 프라이버시와 노출 특성에 직접 영향을 준다.
이런 제어점과 운영 의존성은 클라우드 / AWS 실전 아티클에서 다루는 설계 판단과도 이어진다.
이런 계층 구분 위에서 인프라의 통제·컴플라이언스 요구를 설계할 팀이 필요하다면 클라우드 & 인프라에서, 규제 대응을 포함한 전환 전략이 고민이라면 AX 컨설팅에서 시작할 수 있다.
참고 자료
출처와 각주8개펼치기접기
Footnotes
-
RFC 6066, "Transport Layer Security (TLS) Extensions". ClientHello의
server_name확장과 SNI 형식을 정의한다. https://www.rfc-editor.org/rfc/rfc6066 ↩ -
RFC 9849, "TLS Encrypted Client Hello" (2026-03). ESNI를 대체하는 표준. https://www.rfc-editor.org/rfc/rfc9849 ↩ ↩2 ↩3
-
RFC 7725, "An HTTP Status Code to Report Legal Obstacles" (451). https://www.rfc-editor.org/rfc/rfc7725 ↩
-
Cloudflare SSL/TLS 문서, "ECH Protocol". Inner/Outer ClientHello와
cloudflare-ech.com, 플랜별 활성화 방식을 설명한다. https://developers.cloudflare.com/ssl/edge-certificates/ech/ ↩ ↩2 -
Amazon CloudFront 개발자 가이드, "Supported protocols and ciphers between viewers and CloudFront". 2026-07-16 확인 기준 뷰어 측 ECH 지원이 문서에 명시돼 있지 않다. 문서의 부재만으로 미지원 전체를 단정하지는 않았다. https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/secure-connections-supported-viewer-protocols-ciphers.html ↩
-
NGINX Community Blog, "Encrypted Client Hello Comes to NGINX". NGINX 1.29.4의 ECH 지원과 당시 OpenSSL ECH feature branch 빌드 조건. https://blog.nginx.org/blog/encrypted-client-hello-comes-to-nginx ↩
-
OpenSSL Library, "OpenSSL 4.0 Final Release - Live" (2026-04-14). RFC 9849 ECH 지원이 정식 포함됐다. https://openssl-library.org/post/2026-04-14-openssl-40-final-release/ ↩
-
Mozilla Support, "Understand Encrypted Client Hello (ECH)". Firefox 119부터의 기본 활성화, 암호화 DNS 권장과 기업·가족 보호 환경의 비활성화 조건을 설명한다. https://support.mozilla.org/en-US/kb/understand-encrypted-client-hello ↩ ↩2


