深入了解 TLS 指紋的運作原理:JA3 與 JA4 如何在網路層透過握手特徵識別機器人和中間人代理,以及如何測試並查看你自己的 TLS 握手指紋。
每一個 HTTPS 連線都始於一次握手。在任何應用資料流動之前,你的瀏覽器和伺服器要先協商使用哪些加密演算法——而你的瀏覽器完成這次協商的方式,會產生一個如同 Canvas 雜湊或 WebGL 雜湊一樣獨特的指紋。TLS 指紋識別從握手中提取這個簽章,用以識別發出請求的客戶端軟體,完全不依賴 JavaScript 或 Cookie。在更下面一層,早在 TLS 開始之前,TCP/IP 指紋識別就已從 TCP 握手中暴露了你的作業系統;TLS 指紋識別則從加密層接續下去。
核心重點
- TLS ClientHello 就是指紋。 當你的瀏覽器開啟一個 HTTPS 連線時,它會傳送一個 ClientHello,列出它所支援的密碼套件、擴充功能和橢圓曲線——順序由 TLS 函式庫決定,而非由你決定。
- JA3 將 ClientHello 中的五個欄位雜湊成一個 32 字元的 MD5 字串;相同作業系統上的相同瀏覽器版本會產生相同的 JA3 雜湊。
- JA4 以人類可讀、可排序的格式改進了 JA3,能夠抵禦 GREASE 隨機化和擴充功能重新排序。
- 伺服器將 TLS 指紋與 User-Agent 請求標頭結合,以捕獲偽裝的機器人:一個聲稱是 Chrome 卻帶有 Python
requests握手的請求,是立即引發警覺的紅旗。 - 更改你的 TLS 指紋需要替換或重新設定 TLS 函式庫本身——瀏覽器設定和擴充功能無法觸及它。
趕時間嗎? 查看你自己的即時 TLS 指紋——你的 JA4、密碼套件和 ClientHello,直接從這個連線讀取。
什麼是 TLS 指紋識別?
HTTPS 流量是加密的,但建立連線的過程並非如此。為了建立加密工作階段,客戶端會以明文傳送一個 ClientHello 訊息,其中包含:
- 一個舊版 TLS 版本欄位(通常為
0x0303;TLS 1.3 客戶端在supported_versions擴充功能中指示其真實的最高版本,JA3 並不讀取該欄位) - 它願意使用的密碼套件清單,按偏好順序排列
- 擴充功能清單(伺服器名稱指示、支援的群組、金鑰共享、ALPN 等)
- 用於金鑰交換的支援橢圓曲線(命名群組)
- 支援的橢圓曲線點格式
這些欄位由連結至應用程式的 TLS 函式庫決定——而非由使用者、作業系統或所造訪的網域決定。Chrome 的 BoringSSL、Firefox 的 NSS、Safari 的 Secure Transport 以及 Python 的 OpenSSL 綁定,各自產生可識別的不同 ClientHello。TLS 指紋識別讀取這些差異,並將其轉化為識別碼。
TLS 1.3 標準(RFC 8446)規定了一組必要的擴充功能,但把可選擴充功能的順序以及在允許集合中選擇密碼套件的權利留給了具體實作——而這恰恰是指紋所在之處。
JA3:第一個被廣泛採用的指紋
JA3 由 Salesforce 工程師(John Althouse、Jeff Atkinson 和 Josh Atkins)於 2017 年發布,作為一種簡單、可部署的方式,在網路層對 TLS 客戶端進行指紋識別,無需在客戶端執行任何程式碼。它透過從 ClientHello 中提取五個欄位並以逗號分隔的方式串接來實現:
TLSVersion,Ciphers,Extensions,EllipticCurves,EllipticCurvePointFormats
多值欄位以連字號連接。最終的字串被 MD5 雜湊,產生一個 32 字元的指紋,例如 769a07eb7f17701dd0ad09b9d04ee785。
JA3 的一些特性使其既有用又有局限:
為何有效: 雜湊在工作階段間是穩定的。相同作業系統上相同 Chrome 版本總是產生相同的 JA3,因為 TLS 函式庫在請求間不會改變。
為何失效: JA3 會捕獲擴充功能的順序和密碼的順序,因此一個隨機化排序的實作——或一個新增了某個密碼的瀏覽器更新——就會改變雜湊。MD5 輸出也是不透明的:你僅從指紋本身無法判斷它屬於瀏覽器還是腳本函式庫。當瀏覽器新增對某種擴充功能類型的支援時,即便其底層身份不變,其 JA3 也會隨之改變。
JA4:更穩健的替代方案
JA4 於 2023 年由 FoxIO(JA4+ 套件,由 John Althouse 主導)發布,旨在解決 JA3 的脆弱性。核心設計變化是排序:密碼套件和擴充功能在雜湊之前按其數值(十六進位)進行排序,因此重新排序不會改變指紋。GREASE 值——從 RFC 8701 定義的固定 GREASE 代碼點集合中抽取的保留偽代碼點,瀏覽器傳送這些值以防止中間設備僵化拒絕未識別欄位——在雜湊之前被過濾掉,因此更新了 GREASE 選擇的 Chrome 版本不會使現有簽名失效。
一個 JA4 指紋由一個人類可讀的前綴和兩個雜湊元件組成:
t13d1516h2 _ 8daaf6152771 _ e5627ecdbe6c
│ │ └── 排序後擴充功能清單(已去除 GREASE 和 SNI)
│ │ 加簽名演算法的截斷 SHA-256
│ └────────────────── 排序後密碼套件的截斷 SHA-256
└───────────────────────────────── 可讀前綴:t = TLS,13 = TLS 1.3,
d = 存在 SNI,15 = 密碼數量,
16 = 擴充功能數量,h2 = ALPN(HTTP/2)
僅憑前綴,你就能了解 TLS 版本、是否存在伺服器名稱指示、密碼套件和擴充功能的數量,以及協商的 ALPN——無需參考資料庫即可理解指紋含義。兩個雜湊後綴(密碼清單,然後是擴充功能清單加簽名演算法)進一步縮小到具體實作。
JA3 與 JA4 對比一覽
| 屬性 | JA3 | JA4 |
|---|---|---|
| 輸出格式 | MD5 雜湊(32 字元,不透明) | 人類可讀前綴 + 兩段雜湊 |
| 順序敏感性 | 是——重排序會改變雜湊 | 否——雜湊前先排序 |
| GREASE 處理 | 包含,更新時雜湊會變 | 雜湊前已過濾 |
| TLS 版本可見性 | 否 | 是(在前綴中) |
| 部署年份 | 2017 | 2023 |
在實務上,安全工具和 CDN 邊緣網路開始將兩者並排記錄。
有一個欄位正被這兩種演算法悄悄改寫:後量子金鑰交換。像 X25519MLKEM768 這樣的混合群組,正進入主流瀏覽器的 supported_groups 和 key_share 擴充功能中;由於 JA4 在雜湊前會排序而 JA3 不會,這兩種演算法對這項變化的吸收方式截然不同——詳情參見 ML-KEM 如何改變你的 ClientHello 指紋。
既然你已經能分辨 JA3 和 JA4,以下是我們的邊緣節點針對你當前連線所觀察到的即時 TLS 中繼資料——版本、密碼套件,以及(在可取得時)JA3/JA4 雜湊——全程不做任何儲存:
你的即時 TLS 指紋
讀取自本次連線的 ClientHello —— 不做任何儲存。
正在讀取你的 TLS 握手…
JA3/JA4 雜湊需要在邊緣做深度封包檢測;此處顯示的是本次連線實際暴露的欄位。
需要留意的訊號是一致性:你的指紋是否與你的 User-Agent 所聲稱的瀏覽器相符?不相符的情況——例如一個看起來像 Python requests、卻頂著 Chrome User-Agent 的握手——意味著你網路路徑中的某個東西(企業代理、安全設備或檢查工具)正在重寫你的握手。
伺服器如何使用 TLS 指紋
機器人和自動化偵測
最常見的用途是捕獲聲稱是瀏覽器的自動化工具。Python 的 requests 函式庫、Go 的 net/http、curl 和 Scrapy 各自擁有獨特的 TLS 握手。當一個請求帶有 User-Agent: Mozilla/5.0 (Chrome ...) 請求標頭,但其 JA3 雜湊與 Python 的 OpenSSL 綁定相符時,這種不匹配就是偽裝的直接證據。
這是機器人偵測技術指南中所述指紋不一致檢查的網路層對應物。原理相同:如果兩個可觀察層相互矛盾,其中一個就是在說謊。TLS 指紋識別比基於 JavaScript 的檢查具有一個特殊優勢——它在頁面載入之前就在原始 TCP 串流上執行,使其對機器人不可見。同樣有效的做法是,伺服器還可以檢查 HTTP/2 連線指紋——SETTINGS 值、流量控制視窗增量和偽標頭順序——為偵測增加第二個網路層維度:成功模擬 Chrome TLS 握手的機器人,仍可能被其函式庫在第一個請求之前發出的 HTTP/2 幀所暴露。
VPN 和代理偵測
普通的隧道 VPN(WireGuard、OpenVPN、IPSec)不會改變你的 TLS 指紋:你的瀏覽器仍然建立自己的 TLS 連線,VPN 僅在 IP 層包裝該流量,因此目標伺服器看到的是你瀏覽器真實的 ClientHello。TLS 指紋實際上捕獲的是攔截行為。HTTPS 檢查代理——以及代你終止 TLS 的 VPN 應用程式——使用自己的 TLS 函式庫,產生與任何普通瀏覽器不同的握手。例如,企業 HTTPS 代理會重新加密流量,並呈現一個新的 ClientHello,通常與企業安全產品而非瀏覽器相符。偵測系統可以將此標記為連線正在被攔截的證據。參閱網站如何偵測 VPN 和代理,全面了解 TLS 指紋之外還會檢查哪些內容,包括 IP 信譽、地理位置不符和 WebRTC 洩漏。
安全監控
在防禦端,TLS 指紋識別有助於偵測與命令和控制伺服器通訊的惡意軟體。大多數現成的惡意軟體和漏洞利用框架使用通用函式庫(Python、Go、.NET),即使網域名稱或 IP 位址隨活動而改變,也會產生可識別的 JA3。網路入侵偵測系統會記錄 JA3/JA4 雜湊,並在已知惡意簽名出現在網路上時發出警報。
測試你自己的 TLS 指紋
將上方的即時指紋與你認為自己正在執行的瀏覽器進行比對。BrowserInsight 的機器人偵測工具會呈現其在瀏覽器內的對應物——自動化痕跡與無頭瀏覽器洩漏——而 JA3/JA4 這類網路層數值則需要上方所示的伺服器端檢查,因為它們是從瀏覽器在任何 JavaScript 執行之前傳送的資料中計算出來的。
緩解措施:更改你的 TLS 指紋
與瀏覽器指紋識別不同——後者可以透過切換瀏覽器或啟用隱私模式來部分減少——更改 TLS 指紋需要更改 TLS 函式庫本身,因為指紋是在任何使用者程式碼執行之前由函式庫產生的。
直接使用目標瀏覽器。 最簡單也最可靠的方法。Chrome 的指紋是網路上預期最廣泛的簽名,使用未修改的 Chrome 安裝可確保你的 TLS 握手與伺服器對你的 User-Agent 的預期相符。
使用 TLS 模擬函式庫。 curl-impersonate 等工具會重排密碼套件和擴充功能,以完全匹配目標瀏覽器的握手。它們主要被需要通過 TLS 層檢查的爬取操作人員使用。
使用由真實瀏覽器終止的住宅代理。 某些代理服務透過由實際瀏覽器引擎支援的管道轉發請求,端到端保留瀏覽器的 TLS 指紋。
對於大多數普通使用者來說,TLS 指紋識別是不可見的,無需任何操作。當你執行的自動化程式需要通過真實瀏覽器的檢查,或者你想了解為什麼網路設備或 CDN 會標記你的流量時,它才變得相關。
常見問題
TLS 1.3 會讓指紋識別更難嗎?
在一定程度上會。TLS 1.3 加密了比 1.2 更多的握手內容,包括伺服器的憑證——但 ClientHello 本身因為在共享金鑰存在之前伺服器必須讀取它,所以必然是明文的。ClientHello 仍然包含足夠的變異性來對客戶端進行獨特的指紋識別,而 JA4 正是專為 TLS 1.3 設計的。一個新興擴充功能確實改變了這一格局:加密客戶端問候(ECH) 使用伺服器在 DNS 中發布的金鑰來加密敏感的內部 ClientHello。在 ECH 已部署的地方(Chrome 和 Cloudflare 自 2023-2024 年以來已支援),網路觀察者只能看到一個最小化的外部 ClientHello,這限制了可以被指紋識別的內容。
TLS 指紋識別合法嗎?
在典型情境下,是的。伺服器歷來被允許讀取發送給它們的流量,而 ClientHello 是必須因協定設計而以明文存在的交換部分。使用該資料對客戶端進行分類,與分析 HTTP 標頭沒有什麼不同。
HTTPS 檢查代理能隱藏原始指紋嗎?
不能——它用不同的指紋取代原始指紋。解密並重新加密 HTTPS 流量的代理會呈現自己的 ClientHello,通常與代理軟體的 TLS 函式庫相符,而非使用者的瀏覽器。檢查預期指紋的偵測系統會看到代理簽名,這本身通常也是一個標記。
什麼是 GREASE,它為何會影響 JA3?
GREASE(Generate Random Extensions And Sustain Extensibility,產生隨機擴充功能並維持可擴充性)是 RFC 8701 中定義的一種機制,TLS 實作會在 ClientHello 中通告保留的偽值——從固定的 GREASE 代碼點集合中抽取。其目的是確保伺服器和中間設備不會僅僅因為遇到未識別欄位就拒絕連線——保持協定的可擴充性。由於 JA3 原樣包含這些 GREASE 值,Chrome 更新其 GREASE 選擇就會改變 JA3 雜湊,即便功能上沒有任何變化。JA4 透過在雜湊前過濾掉 GREASE 值來解決這一問題。


