Cloudflare AI 安全稽核系統(三):LLM Agent 的安全反模式——十個讓報告失去公信力的做法

一份有三個真實 MEDIUM 漏洞的報告,
比一份有三十個「潛在可能理論上存在風險」的報告有用十倍。
安全報告的公信力一旦失去,工程師會停止閱讀它。


一、為什麼 LLM Agent 特別容易產生廢話安全報告

LLM 有一個普遍的傾向:它會試圖看起來有幫助

在安全稽核的場景,這個傾向帶來的問題是:

工程師期待的 LLM 行為:
  如果沒找到漏洞 → 說「我沒找到漏洞,這個部分看起來安全」

LLM 實際的行為:
  如果沒找到確定的漏洞 → 說「這裡可能存在潛在的風險...」
                         「雖然沒有明確的漏洞,但理論上...」
                         「建議加強這部分的防護,因為...」

這就是「廢話型安全報告」的根源——它讓 LLM 看起來有在做事,但對工程師毫無價值。

Cloudflare 的 security-audit-skill 明確列出了這個問題的解法,也列出了最常見的反模式。本篇把這些反模式系統化整理,並從 agent pipeline 設計的角度說明如何從根源消除。


二、反模式地圖

十個反模式的分類:

┌─────────────────────────────────────────────────────────────┐
│  類型 A:「看起來嚴格,實際上沒用」的 finding               │
│    反模式 1:OWASP checklist 當 bug list                    │
│    反模式 2:用模糊語言掩蓋不確定性                         │
│    反模式 3:Defense-in-depth 缺失膨脹成漏洞                │
│    反模式 4:部署情境視而不見                               │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│  類型 B:「用量充數」的報告結構問題                         │
│    反模式 5:用 LOW 充厚度                                  │
│    反模式 6:只說壞處,不說好處                             │
│    反模式 7:不提歷史漏洞基準                               │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│  類型 C:「懶惰的調查」導致的誤判                           │
│    反模式 8:太快放棄                                       │
│    反模式 9:不驗證 parser/runtime 行為假設                 │
│    反模式 10:不做根本原因分析就重複報告                    │
└─────────────────────────────────────────────────────────────┘

三、類型 A:「看起來嚴格,實際上沒用」的 Finding

反模式 1:把 OWASP 偏離當成 Bug

症狀:

Finding: "Missing Content-Security-Policy header"
Severity: MEDIUM
Description: "The application does not implement CSP,
              which is recommended by OWASP."

問題:

OWASP 是 checklist,不是 bug list。

「偏離 OWASP 建議」不等於「存在可利用的漏洞」。缺少 CSP 在以下情況根本不重要:

  • 這是一個純 API server,沒有 HTML 回應
  • CSP 在 upstream reverse proxy 已經設定
  • 這個應用沒有 XSS 的攻擊面(沒有 user-controlled output 進入 HTML)

正確做法:

錯的:「沒有 CSP,違反 OWASP」→ MEDIUM

對的:先問「這個應用有 XSS 的攻擊面嗎?」
       ↓ 有 user input 進入 HTML,且有 XSS 漏洞
       → 報告那個 XSS 漏洞(CSP 的缺失在 impact 裡提一筆)
       ↓ 沒有 XSS 的攻擊面
       → CSP 是 hardening note,不是漏洞

如何在 agent 設計中消除:

Hunt agent 的指令要明確:「OWASP 偏離本身不是 finding。你需要找到具體的攻擊路徑。」


反模式 2:用模糊語言掩蓋不確定性

症狀:

"This function could theoretically be vulnerable..."
"An attacker might potentially exploit..."
"There is a possibility that..."
"This may lead to..."

問題:

這些語言是 confirmation bias 的產物——agent 覺得「這裡看起來有問題」,但沒有花時間確認,就用模糊語言留下後路。

對工程師來說,這類 finding 沒有可操作性:

工程師讀到「could theoretically be vulnerable」的反應:
  「我要怎麼修?」→ 不知道
  「這個有多嚴重?」→ 不知道
  「這是真的問題嗎?」→ 不知道
  
  結果:把這條 finding 標記為低優先,下次再說。
  永遠不會修。

正確做法:

二元原則:
  能 exploit → 說具體怎麼 exploit,用什麼輸入,得到什麼結果
  不能 exploit → 說沒有這個漏洞,或說為什麼這個路徑是安全的

沒有「可能」、「也許」、「理論上」的空間。

如何在 agent 設計中消除:

Validation phase 的 agent 被明確指令:拒絕所有包含「potentially」「theoretically」「might」的 finding,除非 finding 中同時有具體的 exploit 示範。


反模式 3:Defense-in-Depth 缺失膨脹成漏洞

症狀:

Finding: "Insufficient output encoding"
Severity: HIGH
Description: "Output encoding is not applied in the template layer.
              An attacker could inject malicious scripts."

(但 input validation 在入口已經過濾了所有 HTML 字符)

問題:

這個 finding 的 severity 是錯的——即使 output encoding 缺失,攻擊者也無法注入,因為 input 在進入系統之前就被清理了。

報告一個「在現有防禦下無法被利用的缺失」,會讓工程師做不必要的工作,並且讓報告的 credibility 下降(「這個 AI 說的東西都是廢話」)。

判斷框架:

問題:如果攻擊者嘗試利用這個缺失,他能成功嗎?

能成功 → 漏洞,報告
  → Severity 看:likelihood × actual impact

不能成功,因為有其他 defense layer → Hardening note
  → 在報告的 "Hardening Recommendations" 區段提及
  → 說清楚:「X 缺失,但 Y 目前防禦了這個攻擊。如果 Y 被移除,X 會變成漏洞。」

不確定 → 繼續調查,不要猜

反模式 4:部署情境視而不見

症狀:

Finding: "No rate limiting on authentication endpoint"
Severity: HIGH
Description: "The /api/login endpoint has no rate limiting,
              allowing brute force attacks."

(但這個服務部署在 Cloudflare Workers 後面,
  Cloudflare 的 WAF 和 Rate Limiting 在邊緣已經啟用)

問題:

孤立地看 codebase 而不考慮部署情境,會產生大量的「在生產環境中實際上已經被保護了」的 finding。

正確做法:

Recon phase 的 Agent 1a 要明確調查部署模型:

調查清單:
  - 有沒有 reverse proxy / CDN?(Cloudflare、Nginx、AWS ALB)
  - 有沒有 WAF?(Cloudflare WAF、AWS WAF、ModSecurity)
  - 有沒有 API gateway?(Kong、AWS API Gateway)
  - 這些組件有沒有提供額外的安全功能?

這些資訊要進入 architecture.md,
所有 Hunt agents 都要看這份文件,
在評估 finding severity 時把部署情境納入考量。

四、類型 B:「用量充數」的報告結構問題

反模式 5:用 LOW 充厚度

症狀:

報告包含:
  - 1 個 HIGH
  - 2 個 MEDIUM
  - 15 個 LOW(missing X-Frame-Options、console.log in production、
                 outdated dependency version、...)

工程師看到 18 個 finding,感覺這份報告很詳盡。

問題:

15 個 LOW 沒有讓報告更有價值——它讓工程師花時間在「很多可能根本不重要的東西」上,降低了他們對真正重要的 HIGH 和 MEDIUM 的關注度。

Cloudflare 的明確原則:

「3 個 MEDIUM 比 10 個 LOW 有用。報告的長度應該反映 codebase 的複雜度,不是用來填充的。」

正確做法:

LOW 的處理方式:
  - 合理的 LOW(真的有 impact,只是機率低)→ 簡短列出,不長篇大論
  - 「感覺應該要修」但無法 exploit → Hardening note,不是 finding
  - 信息量低的 LOW(outdated dep version)→ 可以省略,或只在 appendix 提

原則:報告的品質 = 每個 finding 的平均可操作性

如何在 agent 設計中消除:

Report phase 的 agent 有明確指令:「LOW 需要有明確的攻擊情境才能列入。如果一個 LOW 的攻擊情境是『在非常特定的條件下,攻擊者可以獲得一些額外資訊』,考慮是否真的需要列出。」


反模式 6:只說壞處,不說好處

症狀:

報告裡全是 finding,沒有提到這個 codebase 做得好的地方。

問題:

這讓報告失去可信度——一個工程師看到一份「只有問題、沒有優點」的報告,第一個反應是「這個 AI 只會說問題,不懂我們的系統」。

更重要的是:說明「哪裡做得好」讓工程師知道這份報告有真正讀過程式碼,而不是在亂槍打鳥。

Cloudflare 的明確原則:

「如果 auth 做得很扎實,就明確說出來。這樣你報的問題才有公信力。」

正確做法:

報告結構應包含 "Positive Patterns" 區段:

Positive Patterns:
- Authentication: JWT 驗證有做 algorithm pinning,防止 alg:none 攻擊
- SQL: 所有 database queries 都使用 parameterized statements,無例外
- Secrets: 沒有 hardcoded credentials,全部從環境變數讀取
- Dependencies: 核心依賴都在 latest stable 版本,無已知 CVE

反模式 7:不提歷史漏洞基準

症狀:

Finding 報告:「這個功能可能有 XSS」,但不提這種類型的 XSS 在同類型軟體中是否常見,或者歷史上是否真的被利用過。

問題:

沒有基準的 severity 評估是主觀的。「在理論上可能造成影響」和「這類漏洞在過去 2 年內已被實際利用過 X 次」的 severity 應該是不同的。

正確做法:

Validation phase 的 Baseline Test:

基準問題:
1. 這種漏洞在同類型軟體(同語言、同框架、同功能)中常見嗎?
2. 這種攻擊在 CVE 資料庫或 HackerOne 上有紀錄嗎?
3. 這個 codebase 的實際部署是否有額外的防護讓這個攻擊不切實際?

基準對 severity 的影響:
  常見且有真實案例 → 提升 severity
  理論上可能但無真實案例、攻擊成本極高 → 降低 severity

五、類型 C:「懶惰的調查」導致的誤判

反模式 8:太快放棄

症狀:

Finding 調查過程:
  發現:這個應用用了 parameterized queries
  結論:「沒有 SQL injection」
  
找到第一個 defense 就停下來。

問題:

「有 parameterized queries」不等於「沒有 SQL injection」。

正確的調查方式是繼續追:

追蹤清單(即使有 parameterized queries):
  1. 有沒有任何 sql.raw() 或 .query(string) 的直接用法?
  2. ORM 的 query builder 有沒有允許 raw expression 的方法?
  3. 有沒有動態構建 ORDER BY 或 LIMIT 子句?(這些通常不能 parameterize)
  4. 有沒有 stored procedure 接受 user input?
  5. Migration scripts 或 admin 工具有沒有放鬆限制?

Cloudflare 的明確原則:

「找到一個 defense 不代表調查結束。繼續找可以繞過的方法。」

如何在 agent 設計中消除:

Hunt agent 的指令包含:「找到一個 defense 之後,你的任務是嘗試繞過它。如果確認無法繞過,才能標記這個路徑為安全。」


反模式 9:不驗證 Parser/Runtime 行為假設

症狀:

Finding: "JSON parsing allows prototype pollution"
Severity: HIGH
Description: "User-controlled JSON input could lead to prototype pollution
              because JSON.parse does not sanitize __proto__ keys."

(但這個應用用的是 Node.js 18+,
  V8 的 JSON.parse 在這個版本已經對 __proto__ 做了特殊處理)

問題:

Agent 的假設基於「它認為 JSON.parse 的行為應該是這樣」,而不是「JSON.parse 在這個 runtime 版本的實際行為」。

正確做法:

Parser/Runtime Test 的執行方式:

1. 確認使用的 library / runtime 版本
2. 查閱官方文件或 changelog
3. 如果不確定,明確標注「需要在實際環境中驗證」
4. 不要靠直覺或「我以為是這樣」

常見的假設陷阱:
  - URL parsing 在不同框架的行為差異
  - JSON 特殊 key 的處理(__proto__, constructor)
  - Multipart form parsing 的邊界處理
  - HTTP header 大小寫敏感性
  - Cookie 的 domain matching 規則

反模式 10:不做根本原因分析就重複報告

症狀:

Finding A: "XSS in /api/export endpoint"
Finding B: "Stored XSS via user profile"
Finding C: "Reflected XSS in search results"

(這三個 finding 的根本原因都是:template rendering 缺少 output encoding,
  而這個 template 組件在整個 codebase 都是共用的)

問題:

三個 finding 讓工程師誤以為有三個獨立的問題需要修,但實際上只需要修一個地方:那個 template 組件。

正確做法:

在 Validation phase 的去重步驟:

去重問題:
  「這些 finding 有相同的 root cause 嗎?」
  
  相同 root cause 的定義:
    - 相同的脆弱程式碼(即使被多個地方呼叫)
    - 相同的修復方式
  
  去重後的報告:
    Finding: "Missing output encoding in shared template component"
    Severity: HIGH
    Impact: "Affects /api/export, user profile, search results"
    Fix: "Add output encoding in template-engine.ts:render()"
    
這讓工程師知道:修一個地方,三個問題都解決。

六、這些反模式如何在 Pipeline 設計中被系統性消除

反模式與消除機制的對應:

反模式                          消除機制
────────────────────────────────────────────────────────────
OWASP checklist 思維            Hunt agent 指令:要求 exploit path
模糊語言(potentially/might)   Validation agent:拒絕無 exploit demo 的 finding
Defense-in-depth 膨脹           Validation Test 4(mitigation test)
忽略部署情境                    Recon Agent 1a + architecture.md
LOW 充數                        Report agent 指令:LOW 需要 attack scenario
只說壞處                        Report template:強制包含 Positive Patterns 區段
不做基準比較                    Validation Test 3(baseline test)
太快放棄                        Hunt agent 指令:找到 defense 後繼續嘗試繞過
不驗證 parser 假設              Validation Test 5(parser/runtime test)
重複報告同 root cause           Phase 3 去重步驟(先於 validation)

這個對應關係說明了一件事:這些反模式不是靠「讓 LLM 更聰明」來解決的,而是靠 pipeline 設計——把消除反模式的步驟硬編碼進系統架構裡。


七、從這個系統學到的 Agent Pipeline 設計原則

原則 1:把品質標準編碼進 Phase,而不是靠 Prompt

不好的做法:
  在每個 agent 的 prompt 裡寫:「請不要使用模糊語言」

好的做法:
  設計一個獨立的 Phase,專門負責拒絕模糊語言的 finding

原因:
  前者依賴 LLM 記住並遵守要求(在長 context 下容易忘)
  後者把這個要求硬化成系統結構,不依賴個別 agent 的記憶

原則 2:讓「否定」成為一個明確的任務

大多數 agent pipeline 只設計了「找東西」的 agent
Cloudflare 的系統明確設計了「否定東西」的 agent

Adversarial validation 的 agent 被明確告知:
  「你的任務是推翻這個 finding。假設它是錯的。」

這讓「否定」成為一個 first-class 任務,而不是「找 agent 的附帶工作」

原則 3:Fresh Context 作為最後防線

Phase 6 Independent Verification 的關鍵設計:
  全新的 agent,沒有任何前序 phase 的 context

這不是信任問題——而是因為帶著 context 的 agent 會下意識地
從「我已知的框架」來看問題,而不是真正重新讀一次

Fresh context agent 更容易發現:
  「等等,這行號是錯的」
  「這個函數在這個版本根本不存在」
  「找到的路徑其實不能到達這個 sink」

原則 4:Structured Output 是品質的基礎設施

為什麼 findings.json + schema validation 很重要:

1. 它強迫 agent 明確化每個 finding 的每個維度
   (不能只說「這裡有問題」,要填 severity, confidence, trace, conditions)

2. Validator 在格式錯誤時立刻報錯
   (不讓模糊的 finding 以「格式正確」的假象通過)

3. 跨 run 累積讓系統變得更強
   (每次 run 都基於上次的已知問題,往更深的地方探索)

八、給工程師的實踐建議

如果你在建構自己的 AI 安全稽核系統,或者在評估用 AI 做安全工作的方案:

評估清單:

□ 這個系統有沒有明確拒絕「potentially vulnerable」這類說法?
□ Validation 是否由獨立的 agent 做(而不是 self-review)?
□ 系統有沒有 structured output + schema validation?
□ 有沒有把部署情境(WAF、CDN、proxy)納入分析?
□ 報告有沒有包含「做得好的地方」?
□ 系統有沒有 deduplication before validation?
□ 有沒有機制讓多次 run 累積覆蓋率?

如果以上有超過 3 個「沒有」:
  這個系統可能會產生大量需要人工過濾的 false positives,
  長期使用會讓工程師失去信任。

九、結語:AI 安全工具的成熟度標誌

Cloudflare 這個 repo 最有意思的地方,是它把「反模式清單」和「消除反模式的機制」都公開了。

這代表他們在建這個系統的過程中,犯過這些錯,看到過這些問題,然後系統性地把修正編碼進 pipeline 的設計裡。

這是 AI agent 系統從「玩具」走向「生產可用」的關鍵路徑:

玩具:讓 LLM 做事,看輸出結果
生產可用:
  1. 知道 LLM 會在哪裡出錯
  2. 設計 pipeline 結構來系統性消除這些錯誤
  3. 有 structured output + validation 讓錯誤無法通過
  4. 有 independent verification 作為最後防線

這不是「信任 AI」——這是「設計一個讓 AI 的錯誤在到達你之前就被擋掉的系統」。


系列導覽

  • 第一篇:六階段 Pipeline 全解析
  • 第二篇:Agent 設計深潛——Hunt 策略、Sub-Agent Spawning、Adversarial Validation
  • 第三篇(本篇):LLM Agent 的安全反模式——十個讓報告失去公信力的做法
Yen

Yen

Yen