2361-1

多年來,Cloudflare 一直在DDoS 引擎、WAF和機器人管理等產品中使用先進的指紋識別技術來幫助阻止線上威脅。出於機器人管理的目的,客戶端軟體的指紋特徵元素可以幫助我們快速識別發出 HTTP 請求的軟體類型。這是一種區分瀏覽器和 Python 腳本的有效且準確的方法,同時保護使用者隱私。這些指紋本身用於簡單的規則,它們也支援複雜的機器學習模型。 

確保我們的指紋跟上網路變化的步伐是一項持續且關鍵的任務。機器人總是會嘗試並嘗試看起來更像瀏覽器。瀏覽器的行為會發生重大變化並影響整個網路格局,但這種情況不太常見。去年,谷歌正是這樣做的,使得舊的 TLS 指紋幾乎無法識別最新版本的 Chrome。

JA3指紋 

JA3 指紋由Salesforce 研究人員於 2017 年引入,後來被 Cloudflare 採用,涉及創建 TLS ClientHello 訊息的雜湊。此雜湊值包括 TLS 密碼套件、擴充功能和其他參數的有序列表,為每個客戶端提供唯一的識別碼。 Cloudflare 客戶可以使用 JA3 建立偵測規則並深入了解其網路流量。

2023 年初,Google在基於 Chromium 的瀏覽器中實施了一項更改,以打亂 TLS 擴展的順序,這一策略旨在破壞 JA3 的檢測能力並增強 TLS 生態系統的穩健性。這項修改是由於擔心固定的指紋模式可能會導致嚴格的伺服器實現,從而可能在每次推出 Chrome 更新時導致複雜性。隨著時間的推移,JA3 由於以下原因變得不再那麼有用:

  • TLS 擴充功能的隨機化:瀏覽器開始隨機化其 ClientHello 訊息中 TLS 擴充的順序。這項變更意味著依賴這些擴充功能的順序的 JA3 指紋會隨​​著每個連接的不同而變化,使其無法可靠地識別唯一客戶端。 (更多資訊可以在Stamus Networks找到。)
  • 工具之間的不一致:實現 JA3 指紋識別的不同工具和資料庫通常會產生不同的結果,因為它們處理 TLS 擴展和其他協議元素的方式存在差異。這種不一致阻礙了JA3指紋在可靠的跨組織共享和威脅情報方面的有效性。
  • 範圍有限且缺乏適應性:JA3 僅關注 TLS ClientHello 資料包中的元素,僅涵蓋 OSI 模型層的一小部分。這種有限的範圍常常忽略有關客戶環境的重要背景。此外,隨著 QUIC 等較新的傳輸層協定變得流行,JA3 的方法(最初是為 TLS 的舊客戶端實作而設計的,不包括現代協定)被證明是無效的。

輸入JA4指紋

為了應對這些挑戰,FoxIO開發了JA4,它是JA3 的後繼者,它提供了一種更強大、適應性更強、更可靠的方法,用於跨各種協議(包括QUIC 等新興標準)對TLS客戶端進行指紋辨識。 JA4 於 2023 年 9 月正式推出,是更廣泛的JA4+ 套件的一部分,該套件包括 TLS、HTTP 和 SSH 等多種協議的指紋。該套件旨在可由人類和機器解釋,從而增強威脅檢測和安全分析能力。

JA4 指紋可以抵抗 TLS 擴展的隨機化,並包含其他有用的維度,例如應用層協議協商 (ALPN),這不是 JA3 的一部分。 JA4 的推出受到了網路安全社群的積極歡迎,一些開源工具和商業產品開始將其納入其係統中,其中包括Cloudflare。 JA4 指紋可在BSD 3-Clause 授權下使用,促進從 JA3 的無縫升級。該套件中的其他指紋,例如JA4S(TLS 伺服器回應)和JA4H(HTTP 用戶端指紋),均根據專有的FoxIO 許可證獲得許可,該許可證旨在用於更廣泛的用途,但需要針對商業貨幣化進行特定安排。

讓我們來看看具體的 JA4 指紋範例,代表 Linux 上最新版本的 Google Chrome:

2361-2
  1. Protocol Identifier (t):指示使用 TCP 上的 TLS。此標識符對於確定底層協定至關重要,可將其與QUIC 的q或DTLS 的d區分開來。
  2. TLS Version (13):代表 TLS 版本 1.3,確認客戶端正在使用最新的安全協定之一。版本號碼是透過分析 ClientHello 中支援的最高版本得出的,不包括任何GREASE值。
  3. SNI Presence (d):伺服器名稱指示中存在網域名稱。這表示客戶端指定了網域 (d),而不是 IP 位址(i 表示缺少 SNI)。
  4. Cipher Suites Count (15):反映 ClientHello 中包含的密碼套件總數,不包括任何 GREASE 值。它提供了對客戶願意使用的加密選項的深入了解。
  5. Extensions Count (16):表示客戶端在 ClientHello 中呈現的不同擴充的計數。此度量有助於確定客戶端支援的功能或自訂的範圍。
  6. ALPN Values (h2):表示應用層協定協商協議,在本例中為 HTTP/2,它指示用戶端針對最佳化 Web 效能的協定首選項。
  7. Cipher Hash (8daaf6152771):密碼套件清單的截斷 SHA256 哈希,按十六進位順序排序。這個唯一的雜湊充當客戶端密碼套件首選項的緊湊標識符。
  8. Extension Hash (02713d6af862):排序的擴充清單與簽章演算法清單結合的截斷 SHA256 雜湊。此雜湊提供了一個唯一標識符,有助於根據客戶端支援的擴展和簽名演算法來區分客戶端。

以下是Linux 上最新 Chrome 查詢https://www.cloudflare.com的 TLS ClientHello 的Wireshark範例:

2361-3

將 JA4 支援整合到 Cloudflare 中需要重新考慮解析 TLS ClientHello 訊息的方法,這些訊息之前是在 C、Lua 和 Go 的單獨實作中處理的。認識到需要提高效能並確保記憶體安全,我們開發了一個新的基於 Rust 的包,client-hello-parser。這個統一的解析器不僅透過集中所有相關邏輯來簡化修改,而且還為我們未來的過渡做好準備,例如用即將推出的基於 Rust 的服務取代 nginx。此外,這個簡化的解析器有助於在我們的平台上公開 JA4 指紋,從而改善與 Cloudflare 的防火牆規則、Workers 和分析系統的整合。

解析ClientHello

client-hello-parser 是一個內部 Rust 箱,設計用於解析 TLS ClientHello 訊息。它旨在透過提供一種簡單的方法來解碼和檢查客戶端在建立 TLS 連線時發送的初始握手訊息,從而簡化分析 TLS 流量的過程。該套件有效地使用相關解析欄位填充 ClientHelloParsed 結構,包括版本 1 和版本 2 指紋以及 JA3 和 JA4 雜湊值,這些對於網路流量分析和指紋識別至關重要。

client-hello-parser 函式庫的主要優點包括:

  • 優化的記憶體使用:該程式庫實現了攤銷零堆分配,透過使用dhat crate 進行廣泛測試來追蹤記憶體分配進行了驗證。利用tiny_vec板條箱,它從固定大小數組支援的小向量的堆疊分配開始,僅當這些向量超過其初始大小時才訴諸堆分配。此方法確保所有向量的有效重複使用,維持攤銷的零堆分配。
  • 記憶體安全:由 Rust 強大的借用檢查器增強,並透過廣泛的模糊測試進行補充,這有助於識別和解決先前在 C 實作中未檢測到的潛在安全漏洞。
  • 超低延遲:解析器受益於使用faster_hex進行高效率的十六進位編碼/解碼,它利用SIMD指令來加速處理。 Rust 迭代器的使用也有助於最佳化效能,通常允許編譯器產生 SIMD 最佳化的彙編程式碼。透過使用 BigEndianIterator 進一步提高了效率,它允許在單次傳遞中對 TLS ClientHello 位元組進行高效的串流處理。

解析器基準測試結果:

client_hello_benchmark/parse/parse-short-502
                        time:   [497.15 ns 497.23 ns 497.33 ns]
                        thrpt:  [2.0107 Melem/s 2.0111 Melem/s 2.0115 Melem/s]
client_hello_benchmark/parse/parse-long-1434
                        time:   [992.82 ns 993.55 ns 994.99 ns]
                        thrpt:  [1.0050 Melem/s 1.0065 Melem/s 1.0072 Melem/s]

基準測試結果表明,解析器可以有效地處理不同大小的ClientHello 訊息,較短的訊息以每秒約200 萬個元素的速度處理,較長的訊息以每秒約100 萬個元素的速度處理,展示了SIMD 優化和Rust 的有效性。

強大的測試套件:包括數十個真實的 TLS ClientHello 訊息範例,以及使用JA3和JA4外掛程式針對 Wireshark 驗證的解析元件。此外,具有記憶體清理器的Cargo 模糊器​​可確保不會出現記憶體洩漏或導致核心轉儲的邊緣情況。與遺留 C 解析器的向後相容性測試(作為依賴項匯入並透過 FFI 呼叫)確認兩個解析器產生相同的結果。

與 nginx 無縫整合:編譯為動態庫的 crate 連結到 nginx 二進位文件,確保透過向後相容性測試從舊解析器平滑過渡到新的基於 Rust 的解析器。

基於 Rust 的新解析器的過渡使得跨不同語言(C、Lua 和 Go)的多種實作得以退役,從而顯著增強了效能和解析器針對邊緣情況的穩健性。這項轉變也有助於更輕鬆地整合新功能和業務邏輯,以解析 TLS ClientHello 訊息,簡化未來的擴充和安全性更新。

在我們的網路上實施 Cloudflare JA4 指紋後,我們還剩下另一個問題需要解決。 When JA3 was released, we saw some scenarios where customers were surprised by traffic from a new JA3 fingerprint and blocked it, only to find the fingerprint was a new browser release, or an OS update had caused a change in the fingerprint used by their mobile裝置.透過僅向客戶提供哈希值,客戶仍然缺乏上下文。我們希望為客戶提供必要的背景信息,幫助他們就指紋的安全性做出明智的決定,以便他們能夠快速、自信地採取行動。隨著越來越多的客戶接受人工智慧,我們聽到客戶越來越多地要求破解為我們的機器人偵測提供支援的訊號。這些客戶希望在必須處於控制範圍內的專有資料上運行複雜的模型,但他們希望在執行此操作時能夠獲得 Cloudflare 對網路流量的獨特視角。對我們來說,這兩個用例聽起來像是同一個問題。 

輸入 JA4 訊號 

在不斷發展的網路安全領域,JA3 和 JA4 等傳統指紋辨識技術已被證明對於識別和管理網路流量具有無價的價值。然而,僅這些方法不足以解決惡意代理程式所採用的複雜策略。指紋很容易被欺騙,它們經常變化,並且流量模式和行為不斷變化。這就是 JA4 Signals 發揮作用的地方,它提供了強大而全面的流量分析方法。

JA4 訊號是根據 Cloudflare 在全球範圍內看到的所有流量的最後一小時計算得出的請求間特徵。我們每天都會分析超過1500 萬個獨特的 JA4 指紋,這些指紋是由超過 5 億個用戶代理和數十億個 IP 位址產生的。如此廣泛的數據使 JA4 Signals 能夠提供匯總統計數據,從而更深入地了解全球流量模式 – 遠遠超出單一請求或連接指紋識別所能實現的效果。無論是透過簡單的防火牆規則、Workers 腳本或進階機器學習模型,這些訊號對於增強安全措施至關重要。

讓我們考慮來自防火牆事件活動日誌的 JA4 訊號的具體範例,其中涉及最新版本的 Chrome:

此範例強調,特定 HTTP 請求的機器人分數為 95,這表明它可能源自於操作瀏覽器的人類用戶,而不是自動化程式或機器人。與其他網路用戶端及其各自的 JA4 指紋相比,在這種情況下分析 JA4 訊號可以更深入地了解該客戶端(最新的 Linux Chrome)的行為。以下是我們的客戶在任何請求時都可以看到的訊號範例:

JA4訊號 描述 值範例 解釋
browser_ratio_1h 過去一小時內源自於基於瀏覽器的用戶代理的 JA4 指紋請求的比率。數值越高表示基於瀏覽器的請求比例越高。 0.942 表示此 JA4 基於瀏覽器的請求率為 94.2%。
緩存比率_1h 過去一小時內 JA4 指紋的可緩存響應的比率。數值越高表示可快取的回應比例越高。 0.534 顯示此 JA4 的可快取回應率為 53.4%。
h2h3_ratio_1h 過去一小時內 HTTP/2 和 HTTP/3 請求的比率與 JA4 指紋的請求總數總和。較高的值表示與其他協定版本相比,HTTP/2 和 HTTP/3 請求的比例較高。 0.987 反映 HTTP/2 和 HTTP/3 請求率為 98.7%。
reqs_quantile_1h JA4 指紋的分位數位置是基於過去一小時內所有指紋的請求數。與其他指紋相比,較高的值表示請求數量相對較多。 1 與其他 JA4 相比,請求量較高。

JA4 指紋和 JA4 訊號現在可在防火牆規則 UI、機器人分析和工作人員中使用。客戶現在可以使用這些欄位來編寫自訂規則、速率限制規則、轉換規則或使用 JA4 指紋和 JA4 訊號的 Workers 邏輯。 

讓我們透過以下 Worker 範例示範如何使用 JA4 訊號。此腳本透過解析和分類 JA4 訊號來處理傳入請求,為 Cloudflare Workers 中的進一步分析或規則應用提供清晰的結構:

/**
 * Event listener for 'fetch' events. This triggers on every request to the worker.
 */
addEventListener('fetch', event => {
  event.respondWith(handleRequest(event.request))
})

/**
 * Main handler for incoming requests.
 * @param {Request} request - The incoming request object from the fetch event.
 * @returns {Response} A response object with JA4 Signals in JSON format.
 */
async function handleRequest(request) {
  // Safely access the ja4Signals object using optional chaining, which prevents errors if properties are undefined.
  const ja4Signals = request.cf?.botManagement?.ja4Signals || {};

  // Construct the response content, including both the original ja4Signals and the parsed signals.
  const responseContent = {
    ja4Signals: ja4Signals,
    jaSignalsParsed: parseJA4Signals(ja4Signals)
  };

  // Return a JSON response with appropriate headers.
  return new Response(JSON.stringify(responseContent), {
    status: 200,
    headers: {
      "content-type": "application/json;charset=UTF-8"
    }
  })
}

/**
 * Parses the JA4 Signals into categorized groups based on their names.
 * @param {Object} ja4Signals - The JA4 Signals object that may contain various metrics.
 * @returns {Object} An object with categorized JA4 Signals: ratios, ranks, and quantiles.
 */
function parseJA4Signals(ja4Signals) {
  // Define the keys for each category of signals.
  const ratios = ['h2h3_ratio_1h', 'heuristic_ratio_1h', 'browser_ratio_1h', 'cache_ratio_1h'];
  const ranks = ['uas_rank_1h', 'paths_rank_1h', 'reqs_rank_1h', 'ips_rank_1h'];
  const quantiles = ['reqs_quantile_1h', 'ips_quantile_1h'];

  // Return an object with each category containing only the signals that are present.
  return {
    ratios: filterKeys(ja4Signals, ratios),
    ranks: filterKeys(ja4Signals, ranks),
    quantiles: filterKeys(ja4Signals, quantiles)
  };
}

/**
 * Filters the keys in the ja4Signals object that match the list of specified keys and are not undefined.
 * @param {Object} ja4Signals - The JA4 Signals object.
 * @param {Array} keys - An array of keys to filter from the ja4Signals object.
 * @returns {Object} A filtered object containing only the specified keys that are present in ja4Signals.
 */
function filterKeys(ja4Signals, keys) {
  const filtered = {};
  // Iterate over the specified keys and add them to the filtered object if they exist in ja4Signals.
  keys.forEach(key => {
    // Check if the key exists and is not undefined to handle optional presence of each signal.
    if (ja4Signals && ja4Signals[key] !== undefined) {
      filtered[key] = ja4Signals[key];
    }
  });
  return filtered;
}

JA4 訊號的優點

  • 全面的交通分析:JA4 Signals 匯總一小時內的數據,以提供交通模式的整體視圖。此方法透過分析隨時間變化而不是孤立的變化來增強識別新出現的威脅和異常行為的能力。
  • 異常偵測精確度:利用詳細的請求間特徵,JA4 Signals 能夠精確偵測單一請求指紋辨識可能忽略的異常情況。這可以更準確地識別複雜的網路威脅。
  • 全球可擴展的見解:透過在全球範圍內綜合數據,JA4 Signals 利用了 Cloudflare 網路智慧的優勢。這種廣泛的分析使系統不易受到操縱,並為安全協議提供了彈性基礎。
  • 動態安全實作:JA4 Signals 可以動態通知安全性規則,從簡單的防火牆配置到複雜的機器學習演算法。這種適應性確保安全措施隨著不斷變化的流量模式和新出現的威脅而發展。
  • 減少誤報和漏報:借助 JA4 Signals 提供的詳細見解,安全系統可以更有效地區分合法流量和惡意流量,減少誤報和漏報的發生,並提高整體系統可靠性。

結論

JA4 指紋和 JA4 訊號的推出標誌著 Cloudflare 安全產品(包括機器人管理和DDoS 防護)發展的一個重要里程碑。這些工具不僅增強了我們流量分析的穩健性,也展示了我們網路指紋辨識技術的不斷發展。 JA4 指紋的運算效率可實現對新興威脅的即時偵測和回應。同樣,透過利用聚合統計資料和請求間功能,JA4 Signals 以微秒的速度提供對流量模式的深入洞察,確保沒有任何細節因太小而無法捕捉和分析。

這些安全功能以「每個請求,每一微秒:Cloudflare 的可擴展機器學習」中概述的可擴展技術和開源程式庫為基礎。本討論重點介紹了 Cloudflare 的創新如何不僅分析大量數據,而且還將這種分析轉化為可操作、可靠且動態適應的安全措施。

任何遇到機器人問題的企業都將受益於Cloudflare 獨特的JA4 實施和我們對機器人流量的看法,但運行自己的內部威脅模型的客戶也將受益於從每秒處理超過5000 萬個請求的網路獲取數據見解。請與我們聯繫,以了解有關我們的機器人管理產品的更多資訊。