跳轉到

8.3 安全防護:WAF、Rate Limiting、Turnstile#

本節目標#

  • 用 OWASP Top 10 快速掃描常見攻擊面,並對應到 Cloudflare 的防禦層。
  • 設定 WAF:Managed Rules、自訂 expression、跳轉挑戰(Managed Challenge)。
  • 用 Rate Limiting rule 抵擋暴力與資源濫用。
  • 理解 Turnstile 為什麼是 CAPTCHA 的現代替代品,以及 bot 分數怎麼讀。

OWASP Top 10 精華回顧(網站架構視角)#

# 類別 典型例子 Cloudflare 對應層
A01 Broken Access Control 亂改 URL 存取他人資料 應用程式邏輯(你自己)
A02 Cryptographic Failures 沒有 HTTPS、弱雜湊 Universal SSL(平台)
A03 Injection SQLi、XSS WAF Managed Rules
A04 Insecure Design 可被無限呼叫的昂貴 API Rate Limiting
A07 Ident/Auth Failures 弱密碼、暴力登入 Turnstile + Rate Limiting
A10 SSRF 伺服器替攻擊者打內網 WAF 表達式(擋 worker 內 fetch 目標)

注意:Cloudflare 不是「全防」。A01 與業務邏輯漏洞只能由你自己的程式防,平台的價值是擋下「沒有業務意義的海量攻擊流量」。

WAF:兩種規則心法#

1. Managed Rules(買保險)#

SQLi、XSS、Log4Shell 這類已知攻擊特徵不值得自己寫,用 Cloudflare 維護的全域規則集即可(OWASP Core Ruleset、Cloudflare Managed Ruleset、WordPress 專集等)。重點在調靈敏度:

  • 預設設定:Low 起步,避免 recall 太高導致誤殺(false positive 比 false negative 更常見於新站)
  • 例外處理(Exception):發現某規則誤殺某路徑(如 RESTful API 用了奇怪的 header),針對該規則設 skip,而不是關掉整組

2. 自訂 expression(直覺式 firewall policy)#

Firewall Rules / WAF Custom Rules 的語法叫 wirefilter expression,像過濾器語言:

(http.host eq "api.example.com" and matches(http.request.uri.path, "^/admin"))
(http.user_agent contains "sqlmap" or cf.threat_score gt 20)
(not cf.client.bot and ip.src country in {"KP" "SY"})

動作可選:Block / JS Challenge / Managed Challenge(跳轉挑戰)/ Log / Skip(放行並結束後續規則)。

跳轉挑戰(Managed Challenge)的設計哲學#

請求 ─→ 規則命中 ─→ 不是直接 Block,而是丟出一個「看不見的挑戰頁」
                  ├─ 正常瀏覽器:JS 環境指紋自然通過 → 轉發到源站
                  └─ 腳本/惡意爬蟲:環境不符合 → 卡在挑戰頁

這就是「挑戰」與「封鎖」的經濟學差異:Block 對誤判的代價是無限大(真人進不來),Managed Challenge 把誤判代價降為「多點一次」,把阻塞變成篩選。實務上:可確定的惡性 IP 段才用 Block,難以分辨的灰色語意(削價爬蟲、密碼嘗試)一律 challenge。

Rate Limiting:把 A04 變成平台責任#

Rate Limiting rule(Dashboard → Security → WAF → Rate limiting rules)邏輯是滑動窗口累積:

If: (http.request.uri.path eq "/api/login" and http.request.method eq "POST")
Accumulate requests for: 10 seconds, grouped by: cf.connecting_ip  (取 top 100 維度)
Then: Block for 5 minutes

經典配比:

情境 規則
登入暴力破解 10 秒內同 IP > 3 次 POST /login → block 5 分鐘
貴 API(LLM 轉發) 1 分鐘內同 IP > 10 次 → challenge
全站 1 分鐘 > 300 request → challenge(別太緊,會誤殺 corporate NAT 的公司)

注意分組維度 cf.connecting_ip(夠精準又可用);帳號維度需要應用層自己做(體驗:從你的 Worker 讀 turnstile 詳細結果 + D1 表做第二道閘門)。

Turnstile:CAPTCHA 的現代替代#

傳統 CAPTCHA(識字、選紅綠燈)有三宗罪:打斷人類 UX、被機器學習模型逐漸破解、注意力經濟(reCAPTCHA 被質疑提供免費標註勞動)。

Turnstile 改走被動驗證:

<script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script>
  <div class="cf-turnstile" data-sitekey="0x4AAA..." data-theme="light"></div>
</form>

運作流程:

  1. 頁面載入時,cf-turnstile widget 在辨識與人機互動過程(滑鼠移動、JS 環境完整性、時間行為、WAF 累積的 IP 信譽等)收集訊號。
  2. 多數情況使用者不需點任何東西,幾秒內自動產生 token。
  3. 表單送出時帶 cf-turnstile-response,你的 Worker 用 siteverify API 驗證:
const siteverify = await fetch(
  "https://challenges.cloudflare.com/turnstile/v0/siteverify",
  {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      secret: env.TURNSTILE_SECRET,
      response: form.get("cf-turnstile-response"),
      remoteip: req.cf?.clientIp,
    }),
  }
).then(r => r.json());

if (!siteverify.success) return new Response("Bot?", { status: 403 });
// 若需要更細的判斷:siteverify.cdata / metadata(互動與分數等視指標)

理論核心:驗證「人」不可靠,驗證「環境」可靠。真實瀏覽器 + 真實使用者行為的信號集合很難在雜湊腳本中偽造(瀏覽器指紋 + PoP 看到的數以十億請求建立的信譽模型)。

Bot Fight Mode:免費的最後一道網#

前述 WAF 與 Rate Limiting 都需要你動手寫規則;Bot Fight Mode(Dashboard → Security → Bots)則是零設定的自動防線,免費方案即可開啟:

  • Cloudflare 用全網信譽模型(每天數千億請求的行為資料)自動辨識明顯的惡意 bot,直接攔下,不經你的規則。
  • 另有「確定為 bot 的請求」可透過 cf.client.bot 欄位在自訂規則中引用(例如:允許 Googlebot、擋其他所有 bot)。
  • 免費版的限制:沒有白名單。若它誤殺了你的監控爬蟲或自家測試腳本,只能關閉整個功能或用 Super Bot Fight Mode(付費)做細部豁免。

定位心法:Bot Fight Mode 是「保底」不是「精準打擊」。精密的業務防護仍應回到自訂 expression + Turnstile 三層組合:

第 0 層  Bot Fight Mode       → 自動擋明顯惡意流量(零維運)
第 1 層  WAF Custom Rules     → 業務語意(路徑、國家、UA)
第 2 層  Rate Limiting        → 頻率濫用
第 3 層  Turnstile            → 關鍵動作(註冊、登入、下單)的人機確認

小結與練習#

重點回顧

  1. 知名攻擊(SQLi、XSS、SSRF 特徵)交給 Managed Rules;業務語意用自訂 expression;不可分辨的灰區挑戰,不要 Block。
  2. Rate Limiting = 滑動窗口 + 分組 key,擋的是「重複的惡意濫用」,不是「低頻真用戶」。
  3. Turnstile 被動驗證環境信號,把 UX 打斷降到最低,驗證結果由 Worker 端呼叫 siteverify 才生效(client token 不可信)。

練習

  1. 為 /api/login 寫一組完整防護:1 條 Rate limiting rule + 1 條 Managed Challenge 表達式(當 cf.threat_score > 30)+ 前端 Turnstile,寫出三者的設定語意與先後順序。
  2. 從現實借一對「誤殺情境」:若某 API client 用了非常規 User-Agent 常態呼叫,請評估用 skip、challenge 或 allowlist,並說明取捨。
  3. 關閉 siteverify 步驟(只用前端 widget)會發生什麼安全問題?用一封「重新送出表單 + 偽 token」的 curl 驗證你的推理。