Managed Challenge 與 Bot Score 運作機制

Cloudflare 主動挑戰機制的四代演進、瀏覽器環境探測的檢查項目、Private Access Tokens 的隱私設計,以及為何真人使用者幾乎無感。

📚 系列導覽:本篇是「Cloudflare 反爬蟲機制」系列第 4 篇(完結),對應總覽篇六層偵測中的第 5 層(瀏覽器執行層)。

# 筆記 主題
01 反爬蟲偵測總覽 六層偵測全景、請求時序地圖
02 TLS 指紋:JA3 與 JA4 ClientHello 為何洩漏身分、兩代演算法
03 HTTP/2 指紋與 Akamai Fingerprint 連線層參數指紋、三方交叉驗證
04 Managed Challenge 與 Bot Score(本篇) 主動挑戰機制、PAT、Cookie 綁定

目錄


什麼是 Managed Challenge?

Managed Challenge(受管理挑戰)是 Cloudflare 的一種防護動作:伺服器暫時不回傳網頁內容,而是先要求客戶端證明自己具備真實瀏覽器環境,通過後才放行。

「Managed」(受管理)這個字是重點——挑戰的具體形式不是固定的,而是由 Cloudflare 依當下的訊號動態決定。同一條規則,對不同請求可能產生完全不同的處理:

同一條「Managed Challenge」規則
  ├─ 對真人裝置 → 用 Private Access Token 靜默驗證,或直接放行
  ├─ 對可疑客戶端 → 發出非互動式 JavaScript 挑戰
  └─ 對高風險客戶端 → 發出需要使用者互動的挑戰

這與前兩篇的被動指紋是不同性質的機制。指紋是觀察,挑戰是互動——它會主動要求客戶端做一件事,藉此檢驗指紋看不到的東西。

為什麼需要主動挑戰?

只有被動指紋的限制:

  • 指紋只能證明「你用哪一套函式庫」,不能證明「你有沒有 JavaScript 引擎」
  • 專門模仿指紋的工具存在,被動訊號可能全部對上
  • 無法檢驗 GPU、字型、螢幕、時區這類真實裝置才有的特徵

主動挑戰能檢驗的東西:

  • 是否具備完整的 JavaScript 執行環境
  • 是否有真實的圖形堆疊(GPU 與驅動)
  • 是否處於自動化控制之下
  • 是否願意付出運算成本

核心特點

Managed Challenge = 動態選擇的挑戰形式
                  + 非互動式工作量證明
                  + 瀏覽器環境探測
                  + 跨層一致性檢查

基礎補課:挑戰機制的四代演進

理解演進脈絡,才能理解為什麼現在的機制長這樣。

第一代:CAPTCHA(圖形驗證碼)

做法:顯示一組需要人類辨識的圖形或文字,要求使用者手動完成。

問題:

  • 對真人的成本極高:中斷瀏覽流程,通常需要 10 至 30 秒
  • 無障礙災難:視障使用者難以完成,音訊替代方案體驗更差
  • 對機器的成本反而下降:影像辨識模型的進步讓自動求解越來越容易
  • 商業損害明確:每一次 CAPTCHA 都會流失一部分真實客戶

演進到後期,成本結構完全反轉——對真人很痛,對機器不痛。Cloudflare 於 2021 年公開說明要淘汰 CAPTCHA,並在後續版本中完全移除這個選項。

第二代:JS Challenge / I'm Under Attack Mode

做法:回傳一個中介頁面,顯示「正在檢查你的瀏覽器」,執行一段 JavaScript,約五秒後自動跳轉回原頁面。

特徵:

  • 狀態碼通常是 503
  • 不需要使用者操作,但必然有五秒左右的可見延遲
  • 只檢驗「能不能執行 JavaScript」,檢查深度有限

問題:五秒的固定延遲對正常瀏覽仍是明顯干擾。而且它把成本平均分攤給所有人,包括那些被動訊號早就顯示是真人的請求。

第三代:Managed Challenge(現行主流)

做法:先評估被動訊號,只在必要時才發出挑戰,且挑戰形式動態選擇。

關鍵改變:

  • 大多數真人請求完全不會看到挑戰頁
  • 需要挑戰時優先用非互動式,目標是百毫秒級完成
  • 真實裝置可透過 Private Access Token 靜默通過
  • 只有高風險請求才會遇到需要互動的形式

這一代的核心思想是把成本從「所有人平均分攤」改成「按風險分配」。

第四代:Turnstile(開放給網站自行嵌入)

做法:Cloudflare 把同一套偵測引擎包成一個可嵌入的網頁元件,網站可以放在表單裡(例如註冊、登入、留言)。

與 Managed Challenge 的關係:

Managed Challenge Turnstile
誰觸發 Cloudflare 在邊緣自動插入 網站自己嵌入頁面中
位置 攔在整個頁面之前 表單裡的一個元件
偵測引擎 同一套 同一套
是否需要在 Cloudflare 後面 是 否,任何網站都能用

理解這層關係有助於認識:Turnstile 不是「另一種 CAPTCHA」,而是同一套環境探測邏輯的另一種投放方式。

四代對照

第一代 CAPTCHA         真人成本 極高  │  機器成本 逐漸降低   → 已淘汰
第二代 JS Challenge    真人成本 中    │  機器成本 中         → 舊機制
第三代 Managed         真人成本 近零  │  機器成本 高         → 現行
第四代 Turnstile       真人成本 近零  │  機器成本 高         → 網站自行嵌入

成本結構的變化就是整段演進史:目標始終是拉大「真人成本」與「機器成本」的差距。


Managed Challenge 的決策流程

這張流程圖是本篇最重要的一張圖。

請求進來
   │
   ▼
① 採集被動訊號(零成本,總是執行)
   IP/ASN、JA4、HTTP/2 指紋、標頭一致性、歷史行為
   → 計算 Bot Score
   │
   ▼
② 已有有效的 __cf_bm cookie?
   ├─ 是 ──▶ 直接放行(此連線近期已評估過)
   └─ 否
       │
       ▼
③ 客戶端能提供 Private Access Token?
   ├─ 是 ──▶ 驗證裝置級證明 ──▶ 通過 ──▶ 放行(使用者完全無感)
   └─ 否
       │
       ▼
④ Bot Score 是否足夠高?
   ├─ 是 ──▶ 放行(連挑戰頁都不出現)  ◀── 絕大多數真人走這條
   └─ 否
       │
       ▼
⑤ 發出非互動式挑戰
   回傳挑戰頁 → 客戶端執行 JavaScript → 回傳結果
   ├─ 通過 ──▶ 發放 cf_clearance cookie ──▶ 轉回原頁面
   └─ 失敗或超時
       │
       ▼
⑥ 升級為互動式挑戰,或依規則直接封鎖

這張圖要讀出的三件事

1. 挑戰是最後手段,不是第一步

步驟 ① 到 ④ 都不需要客戶端配合。只有在被動訊號無法決定時,才進入步驟 ⑤。這解釋了為什麼「網站有防護」與「使用者沒感覺」可以同時成立。

2. 有三條「完全無感」的通道

步驟 ②(cookie 快取)、③(裝置證明)、④(分數足夠)都會直接放行。真人使用者幾乎總是命中其中一條。

3. 自動化程式為什麼幾乎必定進入步驟 ⑤ 或 ⑥

  • 步驟 ②:沒有 cookie,因為第一次來
  • 步驟 ③:拿不到 Token,因為需要硬體級裝置證明
  • 步驟 ④:分數不足,因為 JA4 與 HTTP/2 指紋已經顯示不是瀏覽器

三條無感通道全部走不通,於是必然遇到挑戰。這不是針對性的封鎖,而是流程的自然結果。


檢驗項目 1:非互動式工作量證明

做法

挑戰頁載入一段 JavaScript,要求客戶端執行一定量的運算,並在時限內回傳結果。伺服器驗證答案是否正確。

「非互動式」意思是不需要使用者做任何事——沒有圖片要點、沒有文字要輸入,純粹是瀏覽器在背景算完。

為什麼有效:不對稱成本

這是整個設計最優雅的地方:

對單一真人使用者
  一次挑戰 ≈ 幾十到數百毫秒的 CPU 時間
  一天可能遇到幾次
  → 總成本:可忽略

對想抓一百萬個頁面的自動化程式
  一百萬次挑戰 × 每次數百毫秒 CPU
  → 總成本:數十到數百 CPU 小時
  → 加上必須執行 JavaScript,記憶體與程序成本同步上升

**同一個機制,對兩種使用者產生數個量級的成本差異。**這正是工作量證明類機制的核心價值:它不試圖「判斷」誰是機器人,而是讓大規模自動化在經濟上不划算。

附帶效果

工作量證明還順便檢驗了一件事:客戶端是否真的有 JavaScript 引擎。

一個只發 HTTP 請求、不執行 JavaScript 的程式,連題目都無法計算。這一步就篩掉了絕大多數簡易爬蟲——它們不是算錯,而是完全沒有回應。


檢驗項目 2:瀏覽器環境探測

這是挑戰腳本真正做深度檢查的部分。目標是驗證「這是不是一個真實的、非自動化控制的瀏覽器」。

探測項目一覽

類別 檢查對象 為什麼有辨識力
自動化標記 navigator.webdriver WebDriver 標準要求自動化控制時此值為 true
自動化標記 除錯協定的副作用 某些自動化控制方式會留下可觀測的痕跡
圖形堆疊 Canvas 指紋 繪製文字/圖形後取像素雜湊,結果受 GPU、驅動、字型渲染影響
圖形堆疊 WebGL 資訊 可取得 GPU 廠商與型號字串;軟體渲染與真實 GPU 差異明顯
瀏覽器 API window.chrome 等物件 特定瀏覽器獨有的物件,其存在與內部結構都可檢查
瀏覽器 API navigator.plugins、languages、hardwareConcurrency、deviceMemory 真實裝置有合理的值;自動化環境常是空的或不合理
時間特徵 performance.now() 解析度 不同環境的計時器精度不同
時間特徵 JavaScript 執行耗時 執行速度是否符合宣稱的硬體等級
環境資訊 字型清單、螢幕解析度、時區 真實裝置的組合有統計規律
無頭瀏覽器特徵 缺失的 API、渲染差異 無頭模式與有頭模式存在可觀測差異(新版已大幅收斂)

一個特別優雅的檢查:偽造本身可被偵測

假設有人想隱藏自動化痕跡,於是覆寫了那個標記屬性,讓它回報 false。

問題是:覆寫這個動作本身會留下痕跡。

JavaScript 中,原生函式(由瀏覽器引擎以 C++ 實作)與使用者定義的函式,在被要求輸出自身原始碼時表現不同:

原生函式  → 輸出類似「function () { [native code] }」
被覆寫的  → 輸出實際的 JavaScript 原始碼,或其他可辨識的形式

所以檢查腳本不只讀屬性的值,還可以檢查「這個屬性的取值器是不是原生實作」。

這帶出一個很重要的一般性觀念:

隱藏一個特徵,需要修改環境;而修改環境本身也是一個特徵。

這使得環境偽造變成一場層層遞進的競賽——每加一層隱藏,就多一個可被偵測的修改痕跡。這是主動挑戰在架構上的優勢:防禦方可以任意增加檢查項目,而每個新項目都需要偽造方額外處理。

Canvas 與 WebGL 為什麼難偽造

這兩項檢查的本質是要求客戶端證明自己有真實的圖形硬體。

Canvas 指紋
  請客戶端畫一段特定的文字與圖形
  → 取得像素資料的雜湊
  → 結果受 GPU 型號、驅動版本、字型檔案、反鋸齒設定影響
  → 同一段程式在不同裝置上產生不同結果

WebGL 資訊
  可查詢 GPU 的廠商與渲染器名稱字串
  → 真實 GPU 有具體型號
  → 純軟體渲染會顯示軟體渲染器的名稱
  → 在無 GPU 的伺服器環境中,這一點很難掩蓋

要偽造,必須提供一組在統計上合理的值——不只是隨便填一個字串,還要與宣稱的作業系統、瀏覽器版本、螢幕解析度互相吻合。


檢驗項目 3:跨層一致性檢查

挑戰腳本收集到的環境資訊,會與 HTTP 層、TLS 層的宣稱做比對。這是系列第 03 篇三方交叉驗證的延伸——現在變成四方。

TLS 層(JA4)        說:某個 TLS 實作
HTTP/2 層(Akamai)  說:某個 HTTP/2 實作
HTTP 層(UA、Client Hints)說:某個瀏覽器 + 某個作業系統
JavaScript 層        說:某組 GPU、字型、螢幕、時區

     ↓ 四方比對

任何一組矛盾都是訊號

典型的矛盾樣態

矛盾 說明
Client Hints 說是 A 作業系統,WebGL 顯示 B 作業系統的驅動 環境偽造不完整
宣稱是行動裝置,但螢幕解析度與觸控 API 不符 常見於簡易偽裝
時區與 IP 的地理位置差距過大 可能透過代理,也可能是偽造
宣稱高階硬體,但 JavaScript 執行速度異常慢 執行環境與宣稱不符
JA4 顯示某函式庫,但 JavaScript 環境完整 不常見的組合,值得注意

為什麼一致性比單項更難處理

要通過單項檢查,只需要提供一個合理的值。要通過一致性檢查,必須提供一整組互相吻合的值,而且這組值還要與更底層(TLS、HTTP/2)的指紋吻合。

真實裝置天生一致——因為它的 GPU、作業系統、瀏覽器、TLS 函式庫本來就是同一台機器上的真實元件。這個不對稱貫穿整個系列。


Private Access Tokens:裝置級證明

這是現代機制中最值得理解的一塊,因為它是「真人完全無感」的主要原因,而且它的隱私設計相當精巧。

要解決的問題

傳統挑戰的困境:

要確認「這是真人」→ 就要收集裝置資訊 → 但這侵犯隱私
要保護隱私       → 就不能收集資訊   → 但無法確認真人

Private Access Tokens(PAT)用密碼學的方式繞過這個兩難。它建立在 IETF 的 Privacy Pass 架構之上。

四個角色

① Client(客戶端)    使用者的裝置與瀏覽器
② Origin(網站)      需要確認「這是真裝置」的一方
③ Attester(證明方)  作業系統廠商,能驗證裝置真偽
④ Issuer(發行方)    簽發 token 的一方(此情境下是 Cloudflare)

流程

① Origin 需要驗證
      │  「請給我一個 token」
      ▼
② Client 向 Attester 請求
      │  用裝置的硬體金鑰證明:
      │  「我是一台真實的、未被篡改的裝置,
      │    且使用者已登入其帳號」
      ▼
③ Attester 驗證裝置,轉交給 Issuer
      │
      ▼
④ Issuer 用「盲簽章」簽發 token
      │  ← 關鍵:Issuer 簽了名,但看不到自己簽了什麼
      ▼
⑤ Client 把 token 交給 Origin
      │
      ▼
⑥ Origin 驗證簽章有效 → 確認「這是真裝置」→ 放行

隱私設計:三方各自知道的資訊被刻意切開

這是整個設計的核心。盲簽章(blind signature)讓簽署方能對一份資料簽名,卻無法看到資料內容。結果是:

角色 知道什麼 不知道什麼
Attester(作業系統廠商) 這是哪一台裝置 使用者在訪問哪個網站
Issuer(Cloudflare) 有人請求了 token 是哪一台裝置、要訪問哪個網站
Origin(網站) 這是一台真實裝置 是哪一台裝置、是誰

沒有任何單一方能同時知道「誰」與「在看什麼」。 這是一個真正的隱私保護設計,不是行銷說法——它由密碼學保證,不依賴任何一方的承諾。

效果與限制

對真人的效果:

  • 支援的裝置與瀏覽器上,驗證在毫秒級完成
  • 不執行任何 JavaScript 挑戰、不顯示任何頁面
  • 使用者完全不知道發生了驗證

為什麼自動化程式拿不到:

因為第 ② 步需要硬體級的裝置證明——由裝置安全晶片中的金鑰簽署,而該金鑰無法被匯出或模擬。一個跑在雲端虛擬機上的程式無法提供這種證明。

限制:

  • 需要作業系統與瀏覽器支援(目前主要是較新版本的 Apple 平台 Safari,以及 Google 在 Android/Chrome 上的等價機制)
  • 不支援的環境會退回到一般的挑戰流程

為什麼這個機制值得理解

它示範了一個重要的觀念轉變:

從「收集足夠資訊來判斷你是誰」,轉向「用密碼學證明一個屬性,而不揭露身分」。

這與零知識證明的思路相通,也是隱私保護技術的一般方向。


Cloudflare 會設定幾個 cookie,各有明確分工。理解它們的綁定條件,能解釋一個很常見的困惑。

Cookie 用途 典型存活時間 綁定條件
__cf_bm Bot Management。標記「此連線近期已被評估」,避免重複評估 約 30 分鐘 IP + User-Agent(及指紋特徵)
cf_clearance 挑戰通過憑證。持有它可跳過挑戰 由網站設定(數十分鐘到數小時) IP + User-Agent + TLS/HTTP2 指紋
__cfruid 速率限制相關 連線期間 —
__cflb 負載平衡的連線黏著(與 bot 偵測無關) 依設定 —

這是很常見的嘗試,也是很常見的挫折。原因在於綁定:

在瀏覽器中通過挑戰
  → Cloudflare 發放 cf_clearance
  → 同時記錄:發放給哪個 IP、哪個 UA、哪組指紋

把 cookie 複製到程式中使用
  → Cloudflare 驗證 cookie 時重新檢查這些條件
  → IP 或許相同、UA 可以照抄
  → 但 TLS 指紋與 HTTP/2 指紋對不上(前兩篇的內容)
  → cookie 判定無效

關鍵理解:這不是 bug,也不是額外的防護層,而是 cookie 設計上的固有屬性。cf_clearance 證明的是「這個特定的客戶端環境通過了挑戰」,而不是「這個字串可以換內容」。

換個角度看:如果 cookie 可以自由轉移,那麼通過一次挑戰就能大量複製使用,整個機制就失去意義了。綁定是它有效的前提。

__cf_bm 的角色常被誤解

__cf_bm 不是用來追蹤個別使用者的。它的作用是快取一個判斷結果:

第一次請求:完整評估所有訊號(有計算成本)
  → 判定為真人 → 設定 __cf_bm
後續請求(30 分鐘內):看到有效的 __cf_bm → 跳過完整評估

這是效能優化,也是「為什麼真人只有第一次可能感覺到延遲、之後完全順暢」的原因之一。


Bot Score 的組成與級距

級距

分數 定義 含義
1 Automated 確定是自動化程式
2–29 Likely automated 很可能是自動化程式
30–99 Likely human 很可能是真人
0 或無值 — 未計算(方案未啟用或不適用)

輸入訊號

分數由多個來源綜合而成,涵蓋本系列所有篇章的內容:

Bot Score
  ├─ 網路層     IP 信譽、ASN 類型、TCP 指紋      ← 系列 01
  ├─ TLS 層     JA3 / JA4 指紋                  ← 系列 02
  ├─ HTTP/2 層  Akamai 指紋                     ← 系列 03
  ├─ HTTP 層    標頭一致性、Client Hints 完整性   ← 系列 01
  ├─ 執行層     挑戰結果、環境探測結果            ← 本篇
  ├─ 行為層     請求節奏、資源載入模式            ← 系列 01
  └─ 全網統計   相同指紋在其他網站的歷史行為

最後一項值得注意:因為 Cloudflare 同時服務大量網站,它能看到「同一個指紋在其他地方做了什麼」。這是單一網站自建防護無法取得的視角。

WAF 可用的欄位

網站管理者可在自訂規則中使用這些欄位:

欄位 內容
cf.bot_management.score 綜合 Bot Score
cf.bot_management.verified_bot 是否為已驗證的良性爬蟲
cf.bot_management.ja3_hash JA3 指紋
cf.bot_management.ja4 JA4 指紋
cf.bot_management.detection_ids 觸發的偵測規則編號
cf.bot_management.static_resource 是否為靜態資源請求

三個容易誤解的地方

1. 分數是欄位,不是行為

Cloudflare 計算分數,但要不要擋、怎麼擋,完全由各網站自己的規則決定。同樣分數為 3 的請求,在 A 網站被放行、在 B 網站被封鎖,是完全正常的。

2. 分數不是機率

50 分不代表「50% 機率是機器人」。它是一個經過校準的信心指標,級距的分界(1、29、30)比具體數值更有意義。

3. 有一整類「分數低但被歡迎」的流量

Verified Bot 是一個獨立的標記,用於已驗證的良性爬蟲(搜尋引擎、監控服務等)。驗證方式通常是反向 DNS 查詢加上官方公布的 IP 範圍比對。

這些爬蟲的 Bot Score 當然很低——它們確實是機器人——但規則上通常會放行。

這帶出整個系列最實務的一個結論:

Bot 偵測的目的不是「擋掉所有機器人」,而是「區分被授權的機器人與未經授權的機器人」。

因此,取得授權(申請 API、加入白名單、取得資料授權)在技術上永遠是最穩定的路徑——它讓問題從「如何通過偵測」變成「不需要通過偵測」。這不只是合規考量,也是工程上成本最低的解。


為什麼真人幾乎無感

這是使用者最直觀的疑問,答案在於一組刻意的工程取捨。

原因 1:商業誘因

Cloudflare 的客戶是網站經營者,他們最在意的指標是轉換率。每一個多餘的挑戰頁都是流失的客戶。

因此產品設計上有一個硬約束:寧可放過一些低風險的自動化流量,也不要擋到真實使用者。這個約束直接決定了決策流程的形狀——為什麼要有三條無感通道、為什麼挑戰是最後手段。

原因 2:判斷成本被前移

被動訊號(IP、JA4、HTTP/2 指紋)在連線建立階段就採集完成,零額外往返、零額外延遲。

傳統思路:先讓請求進來,再檢查內容,發現可疑就攔下
  → 檢查發生在應用層,需要額外往返

現代思路:在連線握手階段就完成大部分判斷
  → 資訊本來就要傳,採集不增加任何成本

大多數請求在步驟 ① 到 ④ 就決定完了,根本不進入挑戰流程。

原因 3:挑戰本身的延遲預算

即使需要挑戰,非互動式挑戰的目標是百毫秒級完成。使用者感受到的通常只是頁面「閃一下」,而不是明顯的等待。

這與第二代 JS Challenge 的固定五秒是本質差異。

原因 4:結果會被快取

__cf_bm 讓判斷結果在約 30 分鐘內重複使用。即使第一個請求經過完整評估,後續的瀏覽都直接放行。

一個重要的認知修正

❌ 「使用者無感」→ 推論「網站沒有防護」或「防護很弱」

✅ 「使用者無感」→ 推論「判斷成本被前移到連線階段,
                    且成本按風險分配」

無感是精準的結果,不是薄弱的證據。 一個會讓所有人都看到挑戰頁的系統,其實是判斷能力不足——它無法區分風險,只能一律收費。


讀懂挑戰頁與回應標頭

當請求被挑戰攔下時,回應本身包含不少可解讀的資訊。以下為去識別化的代表性樣本。

回應標頭

標頭 意義
server: cloudflare 流量經過 Cloudflare
cf-ray: <id>-<機房代碼> 請求追蹤 ID,結尾三字母是處理該請求的資料中心
cf-mitigated: challenge 這個請求被挑戰攔下(最有價值的單一線索)
server-timing: chlray;desc="<id>" 本次挑戰的追蹤 ID
accept-ch: <清單> 向瀏覽器索取 Client Hints
critical-ch: <清單> 要求瀏覽器立即重送請求並附上這些提示
cross-origin-embedder-policy: require-corp 防止挑戰頁被外部頁面以 iframe 包裝
cross-origin-opener-policy: same-origin 隔離挑戰頁的瀏覽環境
content-security-policy: ... 嚴格限制挑戰頁可載入的資源來源

從標頭能判斷什麼

判斷擋你的是哪一種機制:

403 + cf-mitigated: challenge  → 主動挑戰,理論上可通過
403 + 無 cf-mitigated          → WAF 規則直接封鎖,性質不同
503 + 舊式挑戰腳本             → 第二代 JS Challenge
429                            → 速率限制,與 bot 判定無關

判斷指紋收集的積極程度:

accept-ch 的清單長度是一個訊號。基本的 Client Hints 只有瀏覽器品牌與平台;若清單延伸到 CPU 架構、位元數、裝置型號、完整版本清單,代表有人刻意把裝置資訊的熵值拉到最高——這是經過調整的設定,而非預設值。

搭配 critical-ch 就更明確:它不接受先給一次不完整的請求。

判斷挑戰頁的防護強度:

cross-origin-embedder-policy 與 cross-origin-opener-policy 的存在,是為了封住「用 iframe 包裝挑戰頁、從外部竊取通過憑證」這條路。看到這些標頭,代表挑戰頁本身也做了隔離加固。

挑戰頁內嵌的設定物件

挑戰頁的 HTML 會內嵌一個設定物件(常見名稱為 window._cf_chl_opt),描述本次挑戰的參數。常見欄位:

欄位 意義
cType 挑戰類型:managed / interactive / non-interactive
cRay 本次挑戰的追蹤 ID,對應 Cloudflare 日誌
cZone 目標網域
cITimeS 挑戰發出的 Unix 時間戳
cN 內容安全政策使用的 nonce
cTplV 挑戰頁模板版本
cH、md 等 挑戰的加密參數與挑戰資料,供伺服器端驗證

cType 是其中最有資訊量的一個:它直接說明對方開啟的是哪一級的挑戰。managed 表示需要執行 JavaScript 的完整挑戰,non-interactive 是較輕量的形式。

另外,挑戰頁通常包含一個自動重新整理的設定,讓頁面在停留過久時自我更新——因為挑戰參數有時效性,過期的挑戰無法通過驗證。


對比分析:各種挑戰類型

特性 CAPTCHA JS Challenge Managed Challenge Turnstile
世代 第一代 第二代 第三代 第四代
狀態 已淘汰 舊機制 現行主流 現行(自行嵌入)
需要使用者操作 ✅ 總是 ❌ ❌ 通常不需要 ❌ 通常不需要
真人可見延遲 10–30 秒 約 5 秒 通常無感 通常無感
典型狀態碼 403 503 403 由網站處理
檢查深度 僅「是不是人」 能否執行 JS 完整環境探測 完整環境探測
支援裝置級證明 ❌ ❌ ✅ PAT ✅ PAT
無障礙友善 ❌ 嚴重問題 🟡 尚可 ✅ 良好 ✅ 良好
觸發位置 頁面之前 頁面之前 頁面之前 表單元件內
需在 Cloudflare 後 是 是 是 ❌ 任何網站可用

何時會遇到哪一種?

遇到 Managed Challenge(多數情況)
  → 網站啟用了 Bot Management 或 WAF 規則
  → 你的請求分數不足以直接放行

遇到需要互動的挑戰
  → 分數非常低,或非互動式挑戰失敗
  → 也可能是網站刻意選擇較嚴格的動作

遇到 Turnstile
  → 網站自己在表單裡嵌入的,與邊緣防護是不同層

完全沒遇到挑戰
  → 可能命中了三條無感通道之一
  → 也可能網站根本沒啟用相關規則

常見問題

問題 1:Managed Challenge 就是 CAPTCHA 嗎?

回答:不是,這是最常見的誤解。

CAPTCHA 是第一代機制,必然需要使用者手動操作。Managed Challenge 是第三代,多數情況完全不需要使用者參與——它優先嘗試裝置級證明與非互動式挑戰,只在風險很高時才升級到需要互動的形式。

Cloudflare 淘汰 CAPTCHA 的原因正是它的成本結構已經反轉:對真人越來越痛,對機器越來越不痛。


問題 2:為什麼會卡在「無限挑戰迴圈」?

回答:挑戰通過後需要儲存並回傳 cf_clearance,任何打斷這個循環的因素都會造成迴圈。常見原因:

原因 說明
Cookie 被封鎖 瀏覽器設定或擴充功能阻止設定 cookie,憑證無法保存
IP 在請求之間變動 某些 VPN 或行動網路會切換出口 IP,導致憑證的綁定條件不符
系統時鐘偏差過大 挑戰參數有時效性,時鐘不準會導致驗證失敗
挑戰腳本被阻擋 隱私擴充功能或內容封鎖器攔下了挑戰腳本
第三方 cookie 政策 特定的隔離設定可能影響憑證的儲存

排查順序:先確認 cookie 能正常設定,再檢查系統時間,然後暫時停用瀏覽器擴充功能測試。


問題 3:用無頭瀏覽器一定會被抓到嗎?

回答:不一定,但預設設定下容易被抓到。

真實瀏覽器引擎解決了很多問題——它有完整的 JavaScript 引擎、真實的瀏覽器 API、能執行挑戰腳本。這相對於純 HTTP 客戶端是巨大的進展,也讓 TLS 與 HTTP/2 指紋自然吻合。

但仍有幾個獨立的問題:

  1. 自動化控制本身有標記:WebDriver 標準要求自動化時 navigator.webdriver 為 true
  2. 隱藏標記會留下新痕跡:如前所述,覆寫屬性本身可被偵測
  3. 圖形環境:伺服器環境常缺少真實 GPU,Canvas 與 WebGL 的結果與桌機不同
  4. 其他層獨立運作:機房 IP(第 1 層)與行為模式(第 6 層)不會因為換瀏覽器而改變

所以「用無頭瀏覽器」解決的是第 5 層,不是全部六層。


問題 4:Cloudflare 能藉這些機制識別出「我是誰」嗎?

回答:不能,而且部分機制是刻意設計成不能的。

  • TLS 與 HTTP/2 指紋是群組識別碼:同一版瀏覽器、同一作業系統的所有使用者共享同一個指紋。用它識別個人在原理上就不可行
  • __cf_bm 是判斷結果的快取,不是使用者識別碼
  • Private Access Tokens 用盲簽章刻意切斷關聯:如前所述,沒有任何單一方能同時知道「誰」與「在看什麼」

環境探測(Canvas、WebGL、字型清單)確實產生較高的熵值,理論上有較強的識別潛力——這也是為什麼瀏覽器廠商持續在限制這些 API 的精度。但在 bot 偵測的用途上,這些資訊是用來判斷「這是不是真實裝置環境」,而非建立個人檔案。


問題 5:針對 AI 爬蟲的控制是同一套機制嗎?

回答:不完全相同,那是另一個機制層。

近年除了 Bot Score 之外,還發展出針對 AI 訓練與檢索爬蟲的獨立控制手段——包含集中管理 robots.txt 的宣告、針對已知 AI 爬蟲的分類與封鎖選項,以及讓網站對爬取行為收費的機制。

這些與本篇討論的偵測機制的差別在於:

Bot Score 機制      判斷「這是不是自動化程式」
AI 爬蟲控制機制      判斷「這個已知的爬蟲,網站允不允許 / 收多少費」

後者處理的是授權與商業條件,而非技術偽裝。這也再次呼應前面的結論:整個生態的發展方向是把「機器人存取」制度化,而不是無止境的技術對抗。


問題 6:常見誤解整理

誤解 1:看到 403 就是 IP 被封鎖了。

正確理解:403 加上 cf-mitigated: challenge 表示被挑戰攔下,IP 沒事,換 IP 也沒用。真正的 IP 層封鎖通常表現為連線失敗或特定的錯誤碼頁面。先看標頭再診斷。

誤解 2:拿到 cf_clearance 就能一直重複使用。

正確理解:它綁定 IP、User-Agent 與指紋特徵,而且會過期。設計上就不可轉移——否則通過一次就能無限複製,機制便失去意義。

誤解 3:挑戰頁沒出現,代表網站沒有防護。

正確理解:挑戰頁不出現是「被動訊號已足夠判定」的正常結果。三條無感通道任一條命中都會直接放行。

誤解 4:Turnstile 是 Cloudflare 版的 CAPTCHA。

正確理解:兩者的偵測邏輯完全不同。Turnstile 用的是與 Managed Challenge 相同的環境探測引擎,多數情況不需要使用者操作。它與 CAPTCHA 的相似點只有「都是嵌在表單裡的元件」這個外形。

誤解 5:非互動式挑戰只是形式,沒有真正的防護作用。

正確理解:它同時達成兩件事——篩掉所有不執行 JavaScript 的客戶端(這是絕大多數簡易自動化程式),以及對大規模抓取施加實質的運算成本。它的價值不在於「判斷得多準」,而在於改變經濟結構。


總結

核心要點

Managed Challenge = 動態選擇形式的主動挑戰

決策流程(挑戰是最後手段):
  ① 採集被動訊號 → 算 Bot Score
  ② __cf_bm 有效?      → 放行  ┐
  ③ 有 PAT?            → 放行  ├─ 三條無感通道
  ④ 分數足夠?          → 放行  ┘
  ⑤ 發非互動式挑戰
  ⑥ 升級互動式 / 封鎖

挑戰檢驗三項:
  ├─ 非互動式工作量證明  → 不對稱成本 + 篩掉無 JS 客戶端
  ├─ 瀏覽器環境探測      → 自動化標記、Canvas/WebGL、API 完整性
  └─ 跨層一致性檢查      → 四方比對(TLS / HTTP2 / HTTP / JS)

Bot Score:1 = 自動化,2–29 = 可能自動化,30–99 = 可能真人
           Verified Bot 是獨立標記

理解重點

  1. 挑戰是最後手段:三條無感通道(cookie 快取、裝置證明、分數足夠)讓大多數真人根本不進入挑戰流程。這是「無感」的結構性原因。

  2. 不對稱成本是核心設計:非互動式挑戰對單一真人是幾十毫秒,對百萬次抓取是數十 CPU 小時。它不試圖判斷誰是機器人,而是讓大規模自動化在經濟上不划算。

  3. 隱藏特徵本身是特徵:覆寫自動化標記的動作可被偵測。這使環境偽造成為層層遞進的競賽,而防禦方可以任意增加檢查項目。

  4. PAT 示範了新的思路:用密碼學證明「這是真裝置」這個屬性,而不揭露身分。盲簽章讓三方各自知道的資訊被刻意切開,沒有單一方能同時知道「誰」與「在看什麼」。

  5. Cookie 的綁定是機制有效的前提:cf_clearance 綁定 IP、UA 與指紋,不是 bug 而是設計。可自由轉移的憑證等於沒有憑證。

  6. 無感不等於無防護:一個讓所有人都看到挑戰頁的系統,其實是判斷能力不足——它無法區分風險,只能一律收費。精準的系統才能做到只對可疑流量收費。

  7. 真正的分野是授權,不是技術:Verified Bot 的存在說明目標不是擋掉所有機器人,而是區分授權與未授權。取得授權讓問題從「如何通過偵測」變成「不需要通過偵測」——這在工程上也是成本最低的解。

快速記憶

方面 說明
是什麼 動態選擇形式的主動挑戰機制
「Managed」的含義 挑戰形式由 Cloudflare 依訊號動態決定,非固定
為什麼真人無感 三條無感通道 + 判斷成本前移 + 百毫秒延遲預算 + 結果快取
檢驗核心 是否有真實瀏覽器環境、是否受自動化控制、四層是否一致
最優雅的設計 不對稱成本 + PAT 的盲簽章隱私切分
最有價值的診斷線索 cf-mitigated 標頭與挑戰設定物件的 cType
最大的誤解 以為它等於 CAPTCHA

學習路徑(系列完結)

1. 建立六層全景與請求時序地圖   ✓ 系列 01
2. 理解 TLS 層的第一份證據       ✓ 系列 02
3. 理解 HTTP/2 層的第二份證據    ✓ 系列 03
4. 理解主動挑戰在檢驗什麼        ✓ 本篇

貫穿全系列的一條主線:
  真實瀏覽器的各層天生一致,不需努力;
  模仿方需要讓多個獨立元件同時對應同一目標,且要持續追版本。
  整套偵測體系建立在這個架構性的成本差異上,
  而不是任何單一的聰明演算法。

延伸閱讀


建立日期:2026-09-07 最後更新:2026-09-07

🔗相關文章