了解 HTTP/3 和 QUIC 指紋識別的原理:QUIC Initial 封包中的傳輸參數、h3 ALPN 訊號,以及它與 HTTP/2 指紋識別的差異。
HTTP/2 指紋識別讀取的是客戶端透過 TCP+TLS 傳送的 SETTINGS 幀、視窗增量與偽標頭順序。HTTP/3 把整套握手過程壓縮進單一的 UDP 傳輸協定——QUIC——在此過程中,部分訊號被移到新的位置,另一些被併入了 TLS 層,還有一個訊號被徹底移除。結果是一種與 TLS 指紋識別重疊更多的指紋,但它與 HTTP/2 指紋識別並不完全相同。
核心重點
- QUIC 將傳輸層和 TLS 握手合併為一次往返。 QUIC 客戶端傳送的第一個 UDP 封包——Initial 封包——攜帶一個 TLS 1.3 ClientHello,因此 JA3/JA4 從 TCP ClientHello 中讀取的一切,在這裡同樣存在。
- QUIC 傳輸參數是一種全新的、協定專屬的訊號。 它們作為
quic_transport_parametersTLS 延伸(RFC 9001)搭載在 ClientHello 中,揭露了初始流量控制限制、連線 ID 長度等 HTTP/2 沒有對應項目的實作特定值。 - JA4 已經原生支援 QUIC。 指紋的第一個字元記錄了觀察到的傳輸層:
q代表 QUIC,t代表 TCP,d代表 DTLS——參見 JA4 規範。q13d...指紋可以直接與同一客戶端透過 TCP 產生的t13d...指紋比較。 - HTTP/3 自身的 SETTINGS 幀比 HTTP/2 的更精簡,PRIORITY 幀也已消失——取而代之的是基於標頭的可擴充優先順序方案,而大多數客戶端根本不傳送它。
h3ALPN 值是最早出現的應用層訊號:它出現在與 HTTP/2 的h2相同的 ClientHello 延伸中,只是搭載在 QUIC 而非 TCP 之上。
為何 QUIC 改變了指紋識別的格局
HTTP/2 指紋識別之所以存在,是因為 TCP 和 TLS 是各自獨立的協定:先由 TCP 握手建立連線,再由 TLS ClientHello 協商加密,然後——TLS 完成之後——客戶端才傳送 HTTP/2 自己的啟動幀(SETTINGS、WINDOW_UPDATE、PRIORITY)。三個層,三次識別指紋的機會。
QUIC(RFC 9000)是一種基於 UDP 的傳輸協定,將連線建立和加密合併為一次交換。它的第一個封包——Initial 封包——在一個 CRYPTO 幀中攜帶完整的 TLS 1.3 ClientHello,直接整合進傳輸層握手中(RFC 9001),而非像以往那樣疊加在傳輸層之上。HTTP/3(RFC 9114)則執行在已經建立好的 QUIC 連線之上,大量複用 QUIC 本身已提供的能力——串流多工與流量控制——而不像 HTTP/2 那樣必須為 TCP 重新定義它們。
這種重構意味著 QUIC 客戶端暴露出:
- 一個 TLS 1.3 ClientHello,攜帶在 Initial 封包中——與 JA3/JA4 已經在 TCP 上識別的相同密碼套件、延伸和 ALPN 值。
- QUIC 傳輸參數,這次握手特有的一個新 TLS 延伸。
- 一個小得多的 HTTP/3 SETTINGS 幀,在連線建立後透過單向控制串流傳送。
QUIC 傳輸參數:新出現的訊號
每個 QUIC 客戶端都必須在其 ClientHello 中傳送 quic_transport_parameters 延伸(代碼點 0x39),宣告諸如 initial_max_data、initial_max_stream_data、max_idle_timeout 和 active_connection_id_limit 等值。這些參數描述了客戶端願意支援的連線限制——與 HTTP/2 的 SETTINGS 值類似,實作所選擇的具體預設值在不同 QUIC 函式庫之間存在差異。
Chrome 的 QUIC 協定堆疊、Firefox 的 neqo、quiche(被 curl 和 Cloudflare 使用)、msquic 以及 ngtcp2 各自採用不同的預設傳輸參數。由於這個延伸位於 JA3/JA4 已經在雜湊計算的同一個 ClientHello 中,做指紋識別的伺服器並不需要另建一套蒐集機制——只需在已經讀取的封包中多解析這一個延伸即可。
JA4 已經能識別 QUIC
TLS 指紋識別並不需要為 QUIC 發明新演算法——它只需要多一個欄位。JA4 規範將觀察到的傳輸層記錄為指紋的第一個字元:q 代表 QUIC,d 代表 DTLS,t 代表普通的 TCP 上的 TLS。該字元之後的一切——TLS 版本、密碼雜湊和延伸雜湊——都以完全相同的方式計算,與傳輸層無關,因為它讀取的仍是相同的 ClientHello 結構。
這帶來了一個實際的結果:一個已經在為 TLS 指紋識別檢查 JA4 的伺服器,不需要任何新邏輯就能對 QUIC 連線做指紋識別。它會自動得到一個 q13d... 風格的指紋,並可直接與同一瀏覽器透過普通 TLS 產生的 t13d... 指紋比較——如果同一客戶端的 TCP 和 QUIC 指紋在雜湊部分出現不一致,這本身就是值得調查的訊號。
HTTP/3 更精簡的 SETTINGS 幀
HTTP/3 自己的 SETTINGS 幀(RFC 9114 §7.2.4)在 HTTP 層只定義了 SETTINGS_MAX_FIELD_SECTION_SIZE 一個參數。另外兩個參數——SETTINGS_QPACK_MAX_TABLE_CAPACITY 和 SETTINGS_QPACK_BLOCKED_STREAMS——來自 QPACK,HTTP/3 的欄位壓縮方案(為支援亂序傳遞而對 HTTP/2 的 HPACK 做的重新設計),它們在單獨的規範中定義。相比 HTTP/2 的六個標準 SETTINGS 參數加上一個 WINDOW_UPDATE 幀,這是小得多的暴露面:QUIC 本身已在傳輸層透過上述傳輸參數處理了流量控制和串流限制,因此 HTTP/3 不必像 HTTP/2 在普通 TCP 上那樣,在應用層重新協商這些內容。
不再有 PRIORITY 幀
HTTP/2 的 PRIORITY 幀——已被 RFC 9113 廢棄——在 HTTP/3 的核心幀集中完全沒有對應項目。串流優先順序的設定轉而移到了可擴充優先順序方案,該方案透過 HTTP 的 Priority 標頭欄位(或等效的幀)來表達緊急程度,而非在連線建立之初使用專門的二進位幀。許多客戶端根本不傳送明確的優先順序訊號,而是仰賴伺服器的預設設定——這就去掉了早期 HTTP/2 指紋識別工作中曾經最具辨識度的欄位之一。
與 HTTP/2 指紋識別的差異
| 訊號 | HTTP/2(基於 TCP+TLS) | HTTP/3(基於 QUIC) |
|---|---|---|
| TLS ClientHello | 獨立的 TCP + TLS 握手 | 嵌入在 QUIC Initial 封包中 |
| 傳輸層參數 | 無(TCP 沒有對應項目) | quic_transport_parameters 延伸 |
| SETTINGS 幀 | 6 個標準參數 | 1 個 HTTP 參數 + 2 個 QPACK 參數 |
| 串流優先順序 | PRIORITY 幀(已廢棄) | 可擴充優先順序標頭,常常不設定 |
| ALPN 值 | h2 | h3 |
| JA3/JA4 適用性 | 直接適用(TCP 上的 TLS) | 直接適用,帶有 q 傳輸標記 |
實際結果是:HTTP/3 指紋識別比 HTTP/2 指紋識別更依賴 TLS 層,因為 QUIC 把過去那些獨立的傳輸協商步驟,吸收進了 JA3/JA4 已經在讀取的同一個 ClientHello 中。
在機器人與 VPN 偵測中的應用
偵測系統關注 QUIC 指紋識別有一個具體原因:越來越多存取 Google、由 Cloudflare 提供服務的網站及其他早期採用 HTTP/3 的營運商的瀏覽器流量,預設就是透過 QUIC 而非 TCP 到達。一個圍繞偽造基於 TCP 的 TLS 握手所建構的機器人框架,根本不會產生任何 QUIC 流量——這本身就很有資訊量,因為一個真實的 Chrome 執行個體在存取支援 HTTP/3 的來源站時,通常會協商使用 QUIC 而非退回 TCP。反過來,那些確實支援 QUIC 的工具(透過 quiche 或 msquic 綁定)仍然攜帶著該函式庫特有的傳輸參數預設值和 QPACK 設定,可以像被 SETTINGS 值揭穿的偽造 HTTP/2 客戶端一樣,與真實瀏覽器不帶 quiche 的協定堆疊區分開來。
對於 VPN 和代理偵測,QUIC 帶來了一個新變數:因為它執行在 UDP 之上,一些企業防火牆和較舊的中間設備會直接封鎖它,迫使客戶端退回到基於 TCP 的 HTTP/2。這種回退模式——一個 User-Agent 聲稱是支援 QUIC 的 Chrome 版本,卻從未協商出 h3——本身就是一個可觀察的訊號,與指紋內容本身是分開的。
本文所述的 QUIC 和 TLS 傳輸指紋是在伺服器端觀察到的,因此不會暴露給頁面 JavaScript——但它們與偵測系統從你瀏覽器讀取的客戶端訊號協同運作。你可以用 BrowserInsight 的機器人偵測工具在自己身上查看這些訊號:它會呈現你的 navigator.webdriver 狀態、無頭與自動化痕跡以及指紋一致性,每一項都配有淺白易懂的解釋,且全部在客戶端計算。
常見問題
我需要一個新工具才能看到自己的 QUIC 指紋嗎?
從原理上說不需要——任何伺服器端的 JA4 實作已經能產生一個,因為 QUIC 的 ClientHello 與 TCP 上的 TLS 一樣被解析。改變的是封包擷取發生的位置:QUIC 透過 UDP 到達,因此僅為擷取 TCP 串流而建構的伺服器或中間設備,如果不單獨擷取 UDP,就看不到它。
機器人能透過拒絕使用 HTTP/3 來規避 QUIC 指紋識別嗎?
只能部分做到,而且是拿一個訊號換另一個訊號。退回到 HTTP/2 可以避開 QUIC 特有的指紋識別,但一個真實的、存取支援 HTTP/3 網站的現代瀏覽器通常會協商使用 QUIC——因此一個聲稱是最新版 Chrome 或 Firefox、卻始終不用 QUIC 的客戶端,本身就是值得標記的異常。
像 ECH 這樣加密 QUIC 握手,能隱藏這些訊號嗎?
Encrypted Client Hello 對網路觀察者隱藏了內層 ClientHello 的敏感欄位(例如 SNI),但目標伺服器——它必須解密握手才能提供服務——仍然能看到完整的 ClientHello、傳輸參數和 HTTP/3 SETTINGS。ECH 改變的是客戶端與伺服器之間的被動網路觀察者能看到什麼,並不改變來源站伺服器本身能識別的指紋內容。
HTTP/3 指紋識別今天有 HTTP/2 指紋識別那麼成熟嗎?
目前還沒那麼成熟,主要是因為 QUIC 的採用率雖然在少數大型業者中已相當可觀,但仍小於普遍存在的 TCP+TLS。隨著越來越多來源站和 CDN 預設終止 HTTP/3 連線,可以預期支援 QUIC 的指紋識別會像 JA3/JA4 和 Akamai 指紋一樣,成為機器人偵測體系中的標準組成部分。


