解析 ECH(RFC 9849)的原理:外層與內層 ClientHello、DNS 中的金鑰、電信業者與 CDN 仍能看到什麼,以及如何自我檢查連線。
多年來,HTTPS 連線裡仍以明文點名網站的,只剩 TLS 交握中的 Server Name Indication(SNI)欄位。Encrypted Client Hello(ECH)是標準化的修正方案:瀏覽器把真正的 ClientHello 加密,再包進一個看似無害的外層 ClientHello 裡。但它很容易被誇大。ECH 只是把祕密從「路徑上的所有人」縮小到「負責解密的那台伺服器」,而且交握中仍可見的部分比多數介紹所說的更多。本文說明其機制,並逐方說明每個觀察者能讀到什麼、讀不到什麼。
重點摘要
- ECH 已成為標準。 規範以 RFC 9849 發布(Proposed Standard,2026 年 3 月),此前多年是草案
draft-ietf-tls-esni。 - 一次連線,兩個 ClientHello。 瀏覽器送出一個可讀的外層 ClientHello,其 SNI 填的是面向用戶端的伺服器的
public_name;真正的內層 ClientHello(含真實 SNI 與 ALPN 清單)加密後藏在其中。 - 金鑰來自 DNS。 伺服器的 ECH 設定發布在 HTTPS/SVCB DNS 記錄中(RFC 9460),所以只有這次查詢本身不可被讀取,ECH 才有意義。RFC 明確指出,不加密 DNS 查詢,SNI 加密的價值會大打折扣。
- 網路端仍可能對外層交握做指紋。 外層是真實可讀的 ClientHello,且規範允許用戶端把內層的欄位複製進去,TLS 函式庫的習慣未必會從線路上消失。
- 解密的伺服器能看到一切。 ECH 改變的是網路能看到什麼,而不是 CDN 或來源伺服器能看到什麼。
ECH 要解決的問題
TLS 1.3 加密了伺服器憑證與大部分交握,但 ClientHello 必須在金鑰建立之前就可讀。因此告訴伺服器你要連它眾多主機名稱中哪一個的 SNI,只能以明文傳輸。RFC 稱它「可能是 TLS 1.3 中仍未加密的最敏感資訊」。更早的需求文件 RFC 8744 列出了先前各種 SNI 加密方案為何難以實現,這也是標準直到 2026 年才定稿的原因。關於 SNI 會向 ISP 或 Wi-Fi 營運方洩漏什麼,請見使用 HTTPS 時,電信業者還能看到什麼;本文只談修正方案如何運作。
外層與內層 ClientHello
啟用 ECH 後,瀏覽器會建構兩則訊息:
- **ClientHelloInner(內層)**是真正的交握訊息:真實 SNI、ALPN 清單(宣告 HTTP/2 或 HTTP/3 等協定)以及其他擴充欄位。它以伺服器的公開金鑰透過 HPKE(混合公開金鑰加密)加密,並做填充以減少長度洩漏。
- ClientHelloOuter(外層)看起來是一則普通的 ClientHello。它的
server_name被設為 ECH 設定中的public_name,即面向用戶端的伺服器的名稱,例如某個 CDN 的共用主機名稱。加密後的內層訊息放在encrypted_client_hello擴充欄位中一併送出。
面向用戶端的伺服器會嘗試解密該擴充欄位。成功就以內層 hello 繼續交握,連線通往真實網站;失敗(金鑰不對,或該欄位只是佔位資料)則把外層 hello 當作全部內容,依 public_name 處理。規範描述了兩種部署型態:共用模式,同一服務商既是面向用戶端的伺服器也是來源伺服器;分離模式,面向用戶端的伺服器把解密後的 hello 轉送給另一台後端伺服器。
RFC 直接寫明了一個後果:ECH「會揭露用戶端正在連線到某個服務提供者,但不會揭露是該匿名集合中的哪台伺服器終止了連線」。如果某個網站獨占一個 IP,目的 IP 本身就暴露了它。ECH 的效果取決於有多少網站共用同一個入口。
金鑰從哪來:DNS
瀏覽器需要在連線之前取得伺服器的公開金鑰,而它是從 DNS 取得的。伺服器把 ECHConfigList(一個或多個 ECHConfig,含金鑰、設定 ID 與 public_name)發布在 HTTPS 或 SVCB DNS 記錄中,該記錄類型由 RFC 9460 定義。
這就形成了相依鏈。如果你查詢 HTTPS 記錄的 DNS 請求是明文的,那麼在 ECH 隱藏任何東西之前,你的解析器與路徑上的任何人就已經知道主機名稱了。RFC 還指出,加密 DNS 也能防止區域網路攻擊者竄改 ECH 設定,而且 SNI 加密「在不加密 DNS 查詢的情況下作用有限」。實務上這代表 DNS over HTTPS 或 DNS over TLS。設定與測試方法見 DNS 洩露防護。
誰能看到什麼:三方觀察者對照表
| 路徑上的網路(ISP、Wi-Fi) | 面向用戶端的伺服器(如 CDN) | 其後的來源伺服器 | |
|---|---|---|---|
| 目的 IP | 能 | 能 | 能 |
外層 SNI(public_name) | 能 | 能 | 分離模式:不能 |
| 真實 SNI 與內層 ALPN | 不能(已加密) | 解密後能 | 能 |
| 複製進外層 hello 的其他欄位 | 能 | 能 | 不適用 |
| 完整內層 ClientHello | 不能 | 能 | 能 |
| 明文 DNS 查詢(若使用) | 能 | 不適用 | 不適用 |
有兩列值得強調。第一,真實 SNI 對網路隱藏,但對終止連線的一方從不隱藏。ECH 不等於匿名:CDN 仍然知道你造訪了它的哪個客戶。第二,「其他欄位」這一列最容易讓人意外。
網路端的 JA3/JA4 還能讀到什麼
一種常見的簡化說法(本站的 TLS 指紋核心文章也這樣寫)是:ECH 讓觀察者只剩一則極簡的外層 ClientHello,從而限制指紋辨識。RFC 支持前半句,對後半句則更為謹慎。用戶端建構外層 hello 時遵循一小組規則(提供 TLS 1.3 或更高版本、複製工作階段 ID、使用全新的亂數、把 public_name 放進 server_name),然後依原文,「除 ClientHelloInner.random 外,它可以(MAY)複製內層 ClientHello 的任何其他欄位」。規範在討論匿名集合時還補充說,擴充欄位在 TLS 訊息中的順序等實作細節,即使設定一致,也可能造成外部可見行為的差異。
所以外層 hello 是一則真實的 ClientHello:有密碼套件、擴充欄位、支援的曲線群組與順序,全都能在線路上讀到。被動觀察者可以為這則訊息計算 JA3 或 JA4 類指紋。改變的是指紋描述的是哪一則 hello,以及哪些欄位被隱藏:真實 SNI 與內層 ALPN 清單不再可見,JA4 所記錄的 SNI 有無與 ALPN 取值,也來自外層 hello。外層指紋是否像今天一樣具有區分度,取決於各瀏覽器的實作,RFC 對此並無保證。因此,「ECH 能擊敗網路端 TLS 指紋」尚未獲證實,更準確的說法是「觀察者讀到的變少了」。(QUIC 亦同,見 HTTP/3 與 QUIC 指紋。)
伺服器端則不同。面向用戶端的伺服器會解密內層 hello,看到完整的用戶端指紋,所以在 CDN 層做機器人偵測的廠商不會因 ECH 損失任何資訊。
GREASE ECH:為什麼該擴充欄位本身說明不了什麼
即使瀏覽器沒有某個網站的 ECH 設定,也會送出一個 encrypted_client_hello 擴充欄位,裡面填滿長度合理的隨機位元組。RFC 稱之為 GREASE ECH(概念與 RFC 8701 中的佔位值相同)。它的作用是讓真正的 ECH 不顯眼,使中介設備無法在不破壞一般連線的前提下輕易封鎖「ECH 擴充欄位」。無法解密它的伺服器會直接忽略。
實際的結論是:在封包擷取中看到這個擴充欄位,並不能說明連線受到了保護。只有交握成功且 ECH 被接受才算數。規範也坦白說明,GREASE 與真實 ECH 只對能力有限的被動觀察者才難以區分;外層 SNI、設定 ID 等取值仍可能成為區分依據。
失敗模式:重試、退回與封鎖
- 金鑰過期與 retry_configs。 DNS 快取與金鑰輪替會讓瀏覽器手上的金鑰過期。伺服器無法解密時,會用外層 hello 完成交握,並在加密訊息中回傳新的
retry_configs。用戶端先依public_name驗證憑證,再用新設定重試。規範要求用戶端不要在重試所引發的連線中再次接受retry_configs,因為這代表伺服器設定有誤。 ech_required。 如果用戶端提供了真正的 ECH,連線最終卻沒有用上,用戶端會在送出應用程式資料之前中止,而不是悄悄退回明文 SNI。- 完全沒有設定。 如果 DNS 記錄裡沒有 ECH 參數(或 HTTPS 記錄被剝除),瀏覽器就沒有可用的加密金鑰,只能以舊方式連線;若支援,則會附上 GREASE。此時瀏覽器顯示錯誤還是繼續,屬於產品決策,因此 ECH 應視為盡力而為。
- 不歡迎 ECH 的網路。 RFC 承認,有些營運方可能封鎖真正的 ECH 而放行 GREASE;依賴 SNI 做過濾或負載平衡的受管環境,也可能透過政策停用 ECH。它並未宣稱 ECH 無法被封鎖,只是說它被設計成對被動觀察者而言難以與 GREASE 區分。
本文不提供普及率數字,因為單一數字取決於統計哪些網站與用戶端。想了解大型 CDN 如何部署以及對營運方的意義,可以閱讀 Cloudflare 的文章。
如何自我檢查你的連線
任何透過 Cloudflare 提供服務的網站都有診斷頁 /cdn-cgi/trace。開啟它,看 sni= 這一行:sni=encrypted 表示連線使用了 ECH,sni=plaintext 表示真實伺服器名稱以明文穿過了網路。我們在 2026-10-12 用未啟用 ECH 的用戶端請求 browserinsight.net/cdn-cgi/trace,回傳的是 sni=plaintext,對於沒有已發布金鑰或未啟用 ECH 的用戶端,這是預期結果。此檢查只適用於由 Cloudflare 提供服務的網站,BrowserInsight 自身的工具並不偵測 ECH。
條件滿足,結果就會改善:使用支援 ECH 的瀏覽器,啟用加密 DNS,並造訪已發布含 ECH 設定的 HTTPS 記錄的網站。想了解即便如此仍有什麼會暴露,可以用 IP 情報檢測 查看伺服器看到的位址;無論是否啟用 ECH,目的 IP 對網路始終可見。
常見問題
ECH 會向我的 ISP 隱藏我造訪的網站嗎?
部分隱藏。它隱藏交握中的真實伺服器名稱,但 ISP 仍能看到目的 IP 位址與外層 public_name。對於與許多網站共用 IP 的網站,例如大型 CDN 背後的網站,這確實留下了模糊空間;對於獨占 IP 的網站,光憑位址就能辨識。
ECH 能取代 VPN 嗎?
不能。VPN 改變的是觀察者所處的位置:ISP 只能看到 VPN,而 VPN 服務商能看到原本 ISP 能看到的內容。ECH 對網路隱藏的只是一個欄位,目的 IP、流量時序與流量大小依然可見。
不用加密 DNS,ECH 能運作嗎?
能連上,但隱私效益會減弱。金鑰是從 DNS 記錄取得的,明文查詢會在交握開始之前就把主機名稱暴露給解析器與路徑上的設備。
網站能知道我是否使用了 ECH 嗎?
能。面向用戶端的伺服器知道自己是否解密了 ECH 擴充欄位,CDN 也會對外揭露這一點,其 trace 輸出中的 sni= 一行即是例證。該伺服器可以完整看到內層 ClientHello。
封包擷取裡出現該擴充欄位,就證明用了 ECH 嗎?
不能。瀏覽器在沒有設定時會送出 GREASE ECH,所以未受保護的連線上也可能出現這個擴充欄位。


