iT邦幫忙

2026 iThome 鐵人賽

DAY 17
1
Build on Google AI

從 Vibe Coding 到 Production:用 Google AI 打造上線守門員系列 第 17

Day 17|只報真正能利用的問題:降低 AI 資安報告的誤報

  • 分享至 

  • xImage
  •  

Day 16,我們把 AI 稽核拆成 Recon、Hunt、Validate、Report、Structured Output 與 Independent Verification。

這條 Pipeline 解決了角色混在一起的問題,但還留下一個更實際的問題:

Validator 要看到多少證據,才可以把 Candidate 升級成 Confirmed Finding?

如果門檻太低,報告會充滿理論風險。

如果門檻太高,只有已經造成事故的問題才會被接受。

今天會替 Vibe Guard 定義一組最小證據門檻,並用四個候選問題測試分類結果:

  1. 可重現的跨帳號修改。
  2. 被誤認為正式憑證的 Firebase Web API Key。
  3. 缺少部署層證據的 Rate Limit 問題。
  4. 只出現在本機環境的 Email Log。

目標不是讓報告看起來沒有問題,而是讓每個結論都能回答:

攻擊者是誰、控制什麼輸入、經過哪條路徑、跨越哪個邊界,以及實際得到什麼?

誤報不只是多一列警告

假設 AI 掃描完成後列出 40 個高風險問題。

工程師逐項調查,發現其中:

  • 10 個是測試資料。
  • 8 個已被 API Gateway 阻擋。
  • 7 個無法從外部輸入到達。
  • 6 個只是缺少最佳實務。
  • 5 個使用了錯誤的 Framework 假設。
  • 最後只有 4 個真的成立。

這份報告的 Precision 是:

4 / 40 = 10%

即使它成功找到 4 個漏洞,團隊仍然必須替另外 36 個項目付出調查成本。

更糟的是,連續收到誤報後,Reviewer 容易把下一份報告也當成雜訊。真正嚴重的 Finding 可能因此被延後處理。

所以 AI 稽核的價值不能只看「找到幾個問題」,還要看:

Precision = True Positives / (True Positives + False Positives)

完整評估還需要 Recall。這部分會在 Day 27 建立測試集後實際計算。今天先處理進入報告前的品質門檻。

Candidate、Finding 與 Hardening 不是同一件事

Hunt 階段應該允許 Agent 大膽提出 Candidate。

例如:

Repository 中沒有 Rate Limit Middleware。

這是一項值得調查的觀察,但不能直接改寫成:

攻擊者可以讓正式服務發生 Denial of Service。

兩句話之間缺少部署架構、平台 Quota、CDN、Gateway、身分驗證與容量測試等證據。

Vibe Guard 第一版會使用四種 Verdict:

Verdict 意義 是否進入漏洞清單
confirmed 攻擊路徑與影響都有證據
requires_evidence 假設合理,但關鍵事實未知
hardening 防禦可以改善,但未證明可利用
rejected 已有反證推翻原始判斷

requires_evidencehardening 的差異在於下一步。

requires_evidence 表示只要取得部署設定、執行結果或其他檔案,就可能確認或駁回問題。

hardening 則表示目前已理解行為,也認為可以改善,但沒有足夠的攻擊影響把它稱為漏洞。

Confirmed Finding 的六道證據門檻

今天先定義六項必要證據。

證據 要回答的問題
Attacker Control 攻擊者能控制哪個輸入或狀態?
Reachable Sink 輸入真的能到達危險操作嗎?
Boundary Crossing 行為跨越了哪個授權或信任邊界?
Reproduction 能否用測試、命令或最小 Harness 重現?
Impact 攻擊成功後,資料或服務發生什麼變化?
Mitigation Review 是否有其他防護層阻止這條路徑?

這六項不是適用所有漏洞類型的最終標準,但足以防止第一版 Agent 把單一程式碼味道直接升級成漏洞。

1. Attacker Control

先確認攻擊者真的能控制資料。

看到以下程式:

execFile("git", ["show", commitId]);

不能只因為它執行外部程式就回報 Command Injection。

Validator 還要確認:

  • commitId 是否來自不可信使用者?
  • 呼叫是否使用 Shell?
  • 參數是否被分開傳遞?
  • 上游是否限制為 Commit SHA?

如果值是程式內固定常數,就沒有外部攻擊者可以利用這條路徑。

2. Reachable Sink

輸入出現在程式中,不代表它會到達 Sink。

例如使用者輸入先經過:

output.textContent = userInput;

如果 Agent 只搜尋 userInput 和 HTML 頁面,可能回報 XSS。但 textContent 不會把輸入解讀為 HTML。

Validator 必須追蹤真實資料流,而不是只列出 Source 和附近看起來危險的 API。

3. Boundary Crossing

資安問題通常涉及邊界被突破。

常見邊界包含:

  • 未登入者到已登入功能。
  • 一般使用者到管理員功能。
  • 使用者 A 到使用者 B 的資料。
  • Browser 到 Server。
  • Public Network 到 Internal Service。
  • Development 到 Production。

如果一般使用者只能修改自己的專案,這是正常功能。

如果同一個請求可以修改其他帳號的專案,才構成 Authorization Boundary Crossing。

4. Reproduction

最有力的證據通常是可以重跑的測試:

1. 使用 Alice 建立 Project。
2. 使用 Bob 登入。
3. Bob 指定 Alice 的 Project ID 執行 Update。
4. Update 成功。
5. Alice 重新讀取後看到資料被改寫。

Reproduction 不一定要攻擊正式環境。

它可以是:

  • Unit Test
  • Integration Test
  • Emulator Scenario
  • Parser Harness
  • 最小 HTTP Request
  • Framework 官方規格與對應程式路徑

不能安全執行時,Validator 應明確標示缺少動態證據,而不是假裝已經重現。

5. Impact

「缺少檢查」不是 Impact。

以下句子只描述 Root Cause:

Firestore Rule 沒有比較 ownerId。

Impact 應描述攻擊成功後發生什麼:

任何已登入帳號都能讀取並改寫其他使用者的 Project Name 與 Launch Goal。

這樣團隊才能判斷資料敏感度、受影響範圍與修復優先級。

6. Mitigation Review

Repository 不是完整 Production Environment。

即使應用程式沒有某項防護,其他層仍可能包含:

  • Cloud Armor 或 WAF
  • API Gateway
  • Firebase Security Rules
  • App Check
  • IAM
  • Network Policy
  • Platform Quota
  • Load Balancer Timeout

Validator 不應假設這些防護一定存在,也不能假設它們一定不存在。

正確結果是:

Requires Evidence:需要部署設定確認 Edge Layer 是否限制請求。

未知資訊必須維持未知。

用四個候選測試證據門檻

今天的 Demo 放在:

demo-app/finding-quality-demo/scenario.js

進入專案:

cd /media/mickey/777/ithome/demo-app

執行:

npm run finding-quality:demo

程式會逐項檢查六道 Evidence Gate,再將 Candidate 分類。

AUTH-01:Confirmed

第一個候選是 Day 7 已經重現的越權問題:

Any signed-in user can modify another user's project

它具備完整證據:

門檻 證據
Attacker Control Bob 可以指定另一筆 Project Document ID
Reachable Sink 請求到達 Firestore Write
Boundary Crossing Bob 修改 Alice 擁有的 Resource
Reproduction 兩個測試帳號在 Emulator 中成功重現
Impact Project Name 與 Launch Goal 被改寫
Mitigation Review Rules 唯一條件是使用者已登入

因此輸出:

AUTH-01 CONFIRMED

這個 Finding 不依賴「看起來危險」。它具有完整攻擊敘事與可重跑證據。

SECRET-01:Rejected

第二個候選把以下內容判定為 Credential Leak:

apiKey: "demo-api-key"

但 Day 16 已確認:

  • 這是 Firebase Emulator 的 Project Identifier。
  • 它不是 Gemini API Key。
  • 應用程式拒絕在非本機環境執行。
  • 沒有證據顯示它能授權存取正式 Firestore。

因此輸出:

SECRET-01 REJECTED

注意,Rejected 不代表刪掉紀錄。

系統應保存候選、原始理由與反證。未來相同字串再次被 Hunter 找到時,可以避免重複調查。

RATE-01:Requires Evidence

第三個候選指出 Repository 沒有 Rate Limit Middleware。

我們確實能證明:

  • Client 可以持續送出請求。
  • 請求可以到達應用程式。

但目前不能證明:

  • Production Endpoint 沒有 Gateway 限制。
  • 平台 Quota 不會阻止流量。
  • 攻擊負載足以造成資源耗盡。
  • 合法使用者會因此受到影響。

因此輸出:

RATE-01 REQUIRES_EVIDENCE

下一步不是立刻修 Code,而是取得 Deployment Configuration 並執行受控容量測試。

LOG-01:Hardening

第四個候選是本機開發 Log 顯示使用者 Email。

Email 屬於應該謹慎處理的個人資料,因此減少 Log 內容是合理改善。

但目前資料只出現在本機 Demo,也沒有證據顯示未授權的人可以讀取這份 Log。

因此輸出:

LOG-01 HARDENING

這不是忽略隱私風險,而是把修正建議與已確認漏洞分開。

完整執行結果

Demo 最後輸出:

SUMMARY
confirmed: 1
requires_evidence: 1
hardening: 1
rejected: 1

如果沒有證據門檻,四個候選可能全部被寫進漏洞報告。

加入分類後,漏洞清單只留下已經證明能跨越權限邊界的 AUTH-01

其他結果沒有消失,而是進入符合目前證據狀態的 Queue。

Severity 與 Confidence 必須分開

另一個常見錯誤,是把 Severity 當成證據強度。

兩者回答不同問題:

  • **Severity:**如果問題成立,影響有多大?
  • **Confidence:**目前證據有多充分?

例如公開管理介面可能具有 Critical Impact,但如果 Agent 尚未確認 Route 是否真的部署,就只能是低 Confidence 的 Candidate。

相反地,本機 Log 暴露一個測試 Email 可以有高 Confidence,但 Severity 很低。

Structured Finding 應分開保存:

{
  "severity": "high",
  "confidence": "high",
  "verdict": "confirmed"
}

不能用「可能很嚴重」補償「尚未證明」。

不要讓模型用文筆填補證據

LLM 很擅長把不完整觀察寫成完整敘事。

輸入:

沒有 Rate Limit Middleware

模型可能擴寫成:

攻擊者可大量呼叫 API,耗盡 Server Resource,
造成所有使用者無法存取服務。

這段話語意合理,卻增加了原始資料中不存在的事實。

Validator 的 Prompt 應要求:

  1. 每項事實連回 File、Symbol、設定或執行結果。
  2. 無法證明的欄位使用 unknown
  3. 缺少必要證據時不得輸出 confirmed
  4. 明確列出已搜尋但未找到的防護層。
  5. 保存能推翻 Finding 的反證。

好的報告不是把空白補滿,而是讓讀者看見證據在哪裡停止。

靜態分析與動態驗證要互補

只靠靜態搜尋,容易漏掉 Runtime 行為。

只靠動態測試,也可能只覆蓋一條路徑。

Vibe Guard 應組合兩種證據:

Static Trace
Source → Validation → Authorization → Sink

Dynamic Proof
Setup → Attack Input → Observed Result → Cleanup

AUTH-01 為例:

  • Static Trace 說明 Rules 沒有檢查 Owner。
  • Dynamic Proof 證明第二個帳號真的能改寫資料。

兩者一起使用,比單獨看到寬鬆 Rule 更可靠。

Validator 的停止條件

Agent 不能無限制搜尋證據。

第一版可以使用以下停止條件:

Confirmed
六項證據齊全,且沒有有效反證。

Rejected
找到足以推翻核心攻擊假設的反證。

Requires Evidence
必要證據位於目前不可見的系統或需要人工授權。

Hardening
行為已理解,但沒有可證明的安全邊界與影響。

這讓每次驗證都有明確出口,也讓後續流程知道該要求程式修正、補充設定,還是單純排入改善清單。

今天的結論

AI 找到可疑程式碼不等於找到漏洞。

一個 Confirmed Finding 至少要說清楚:

  1. 攻擊者能控制什麼。
  2. 輸入如何到達危險操作。
  3. 哪個信任或授權邊界被跨越。
  4. 如何重現。
  5. 實際影響是什麼。
  6. 其他防護層是否已經阻止攻擊。

今天的 Demo 把四個候選分成 Confirmed、Requires Evidence、Hardening 與 Rejected,各自保留正確的證據狀態。

降低誤報不是少找問題,而是不讓推測冒充事實。

明天,我們會正式實作 Recon:讓 Agent 在開始 Review 前,先建立系統架構、資料流、信任邊界與部署假設。

參考資料


上一篇
Day 16|拆解 Cloudflare Security Audit Skill:為什麼找漏洞不能只問一次 AI?
下一篇
Day 18|從 Recon 開始:讓 Agent 看懂系統架構與信任邊界
系列文
從 Vibe Coding 到 Production:用 Google AI 打造上線守門員18
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言