iT邦幫忙

2026 iThome 鐵人賽

DAY 18
1
Build on Google AI

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

Day 18|從 Recon 開始:讓 Agent 看懂系統架構與信任邊界

  • 分享至 

  • xImage
  •  

Day 17,我們替 Confirmed Finding 定義了六道證據門檻。

不過,Validator 要判斷攻擊者是否跨越邊界,前提是 Agent 已經知道系統有哪些邊界。

如果一開始就把 Repository 丟給 AI,要求它「找出所有漏洞」,模型很容易把看到的程式碼片段直接套進常見漏洞清單:

看到 API Key → Secret 洩漏
看到 innerHTML → XSS
沒有 Rate Limit Middleware → DoS
只檢查已登入 → 授權安全

這些判斷可能成立,也可能完全忽略執行環境、資料來源與其他防護層。

今天會替 Vibe Guard 實作 Recon 的第一版,讓後續 Hunter 開始找問題前,先取得:

  1. 應用程式用途與技術棧。
  2. 使用者、系統與維運角色。
  3. 輸入面與資料流。
  4. Authentication 與 Authorization 的執行位置。
  5. 信任邊界。
  6. Repository 無法回答的問題。

最終產物不是一段「看起來合理」的系統介紹,而是一份每項事實都能連回檔案與行號的 architecture.json

Recon 不是列出目錄

目錄清單可以告訴 Agent 專案有哪些檔案,卻無法回答:

  • 誰會使用這個系統?
  • 哪些資料由外部進入?
  • 身分在哪裡被確認?
  • 權限在哪一層被執行?
  • 敏感資料會流向哪裡?
  • 哪些防護存在 Repository 外?

Cloudflare security-audit-skill 的 Reconnaissance 階段把工作分成三個方向:

方向 要回答的問題
Overview 這是什麼應用程式、使用什麼技術、入口在哪裡?
Trust Model 有哪些角色、信任邊界、認證與授權控制?
Input Surface 外部輸入從哪裡進入,又會到達哪些 Sink?

最後才把三者合併成 architecture.md,提供給後續每一個 Hunter。

這個順序很重要。Recon 的目標不是找漏洞,而是建立後續判斷共用的系統模型。

先分清楚事實、推論與未知

LLM 很容易把合理推論寫成確定事實。

例如 Repository 中有 firebase.json,只能證明專案存在 Firebase Hosting 設定,不能直接證明:

Production 正部署在 Firebase Hosting。

可能的情況還包含:

  • 這份設定只供本機 Emulator 使用。
  • 正式環境改由其他 Pipeline 部署。
  • 專案根本還沒有正式環境。
  • CDN、WAF 與 IAM 設定只存在 Cloud Console。

因此 Recon 產物應區分三種狀態:

狀態 定義 範例
Observed Repository 有直接證據 Client 呼叫 connectFirestoreEmulator
Inferred 由多項證據推導,仍需標示假設 應用程式看起來是單頁 Web App
Unknown 目前沒有足夠資料 正式環境是否啟用 App Check

第一版 Demo 會優先保存 Observed Fact,並把 Repository 外的資訊列入 unknowns,不讓模型自行補完。

今天要 Recon 的 Demo

Vibe Guard 的測試專案是一個只允許在本機執行的 Firebase Web App。

使用者可以:

  1. 使用 Email 與 Password 建立帳號。
  2. 登入 Firebase Authentication Emulator。
  3. 把 Project Name 與 Launch Goal 寫入 Firestore Emulator。
  4. 監聽並顯示 projects Collection。

這段描述目前只是人類理解。Agent 還需要知道每項結論的證據位置。

今天的程式放在:

demo-app/recon-demo/scenario.js

進入 Demo:

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

執行:

npm run recon:demo

程式會讀取:

package.json
firebase.json
firestore.rules
src/main.js

再輸出:

recon-demo/architecture.json

讓每項結論帶著證據

Recon 最危險的失敗,不是少寫一段介紹,而是產生無法核對的結論。

Demo 使用 evidence() 尋找來源:

function evidence(path, snippet) {
  const lines = sources[path].split("\n");
  const lineIndex = lines.findIndex((line) => line.includes(snippet));

  if (lineIndex === -1) {
    throw new Error(`Missing Recon evidence in ${path}: ${snippet}`);
  }

  return {
    path,
    line: lineIndex + 1,
    snippet: lines[lineIndex].trim()
  };
}

每項 Observed Fact 都保存:

  • path
  • line
  • snippet

如果程式改動後找不到原始證據,Recon 會直接失敗,不會繼續輸出一份過期架構。

這還不是完整的 Semantic Code Analysis,但已經建立一個重要規則:

沒有來源的架構描述,不能被後續 Agent 當成事實。

第一張圖:應用程式與技術棧

Agent 先確認應用程式類型、用途與主要入口。

Demo 的輸出包含:

{
  "type": "Local-only Firebase web application",
  "purpose": "Users sign in and save project launch goals.",
  "stack": [
    "Vite",
    "Firebase Authentication",
    "Cloud Firestore"
  ]
}

package.json 的 Vite Script、src/main.jsinitializeApp(),以及 Firebase 設定共同支持這項判斷。

知道技術棧後,Hunter 才能選擇正確的檢查方式。

例如這個專案:

  • 沒有傳統 Express Route。
  • Browser 直接呼叫 Firebase SDK。
  • 資料存取權限主要由 Firestore Security Rules 執行。
  • Authentication 與 Firestore 都連到本機 Emulator。

如果 Agent 只搜尋 Server Middleware,會錯過真正的授權邊界。

第二張圖:角色不是只有「使用者」

Recon 先列出三種角色:

Actor 預期能力
Visitor 註冊或登入
Authenticated User 建立與查看 Project
Operator 未知,Repository 沒有正式 IAM 或 Runbook

第三列刻意保留 Unknown。

Repository 沒有足夠資料判斷:

  • 誰可以讀取 Production Database。
  • 誰可以查看 Log。
  • 誰可以執行 Restore。
  • 誰可以修改 Security Rules。

如果 Agent 自行假設「只有管理員可以」,後續隱私與維運檢查就會建立在虛構前提上。

第三張圖:輸入面要一路追到 Sink

只列出 Form 不夠。Recon 還要描述輸入最後到哪裡。

Demo 找到三個主要輸入面:

Input Sink 後續應檢查什麼?
Email、Password Firebase Authentication 憑證處理、錯誤訊息、濫用防護
Project Name、Launch Goal Firestore projects 驗證、授權、資料最小化
Firestore 中的 Project 欄位 Browser innerHTML Stored XSS、安全輸出

第三項特別容易被忽略。

外部輸入不一定在同一個 Request 中到達 Sink。使用者先把內容存進 Database,另一個使用者之後再讀取並渲染,仍然是一條完整資料流:

Project Form
  → Firestore SDK
  → Security Rules
  → projects Collection
  → Snapshot Listener
  → innerHTML

Recon 不在此階段直接宣布 XSS。

它只把 Source、Storage、Control 與 Sink 連起來,交給 Hunter 建立攻擊假設,再由 Validator 實際重現。

第四張圖:Authentication 不等於 Authorization

Demo 使用:

onAuthStateChanged(auth, (user) => {
  // 切換登入後介面
});

這能證明 Browser 會追蹤登入狀態。

真正控制 Database 存取的是:

firestore.rules

目前 Rule 為:

allow read, write: if request.auth != null;

Firebase 官方文件說明,未登入時 request.authnull;登入後可以使用 request.auth.uid 對照資料擁有者。

因此 Recon 應記錄:

Authentication:
Browser 使用 Firebase Authentication 取得身分。

Authorization:
Firestore Security Rules 決定 projects 是否可讀寫。

Observed policy:
任何已登入身分都符合目前 read/write 條件。

最後一句仍然不是完整漏洞報告。是否能跨帳號讀寫,還要結合 Collection Query、資料所有權模型與動態測試。

第五張圖:信任邊界

信任邊界是資料從一個控制範圍進入另一個控制範圍的位置。

第一版 Recon 找到三條:

Browser
  → Firebase Authentication Emulator

Browser
  → Cloud Firestore Emulator

Repository Configuration
  → Firebase Hosting

每條邊界都要記錄:

  • from
  • to
  • 跨越邊界的資料
  • 執行控制的程式或設定
  • 證據位置

以 Browser 到 Firestore 為例:

{
  "from": "Browser",
  "to": "Cloud Firestore emulator",
  "data": "Project records and authenticated identity",
  "control": {
    "path": "firestore.rules",
    "line": 6,
    "snippet": "allow read, write: if request.auth != null;"
  }
}

這份資訊會直接影響 Hunter 的優先順序。

如果高價值資料跨越 Browser 與 Database 邊界,而唯一控制只確認「已登入」,Access Control Hunter 就應優先追蹤跨帳號操作。

不要把看不到的部署層當成不存在

Repository 中沒有 WAF 設定,不代表正式環境沒有 WAF。

Repository 中沒有 Backup Script,也不代表平台沒有 Managed Backup。

反過來,Agent 也不能假設這些防護一定存在。

Demo 把無法回答的問題列成:

{
  "question": "Are CDN, WAF, rate limits, quotas, or App Check enabled?",
  "requiredEvidence": "Firebase and Google Cloud project configuration"
}

完整 Unknown 清單包含:

  1. Production Environment、Project 與 Region。
  2. CDN、WAF、Rate Limit、Quota 與 App Check。
  3. Log 與 Backup 的 IAM。
  4. Data Retention 與 Deletion Requirement。

這些項目不是 Recon 失敗,而是 Recon 的重要產物。

後續遇到依賴這些資訊的 Candidate 時,Validator 應輸出 requires_evidence,而不是自行選擇最危險或最安全的假設。

完整執行結果

執行後會看到:

RECON SUMMARY
Application:      Local-only Firebase web application
Actors:           3
Trust boundaries: 3
Input surfaces:   3
Data flows:       3
Unknowns:         4

並列出三條信任邊界與四個待補問題。

產生的 architecture.json 可以成為下一階段的共用 Context:

Repository
  → Recon
  → architecture.json
  → Security Hunter
  → Privacy Hunter
  → Reliability Hunter

不同 Hunter 不需要各自猜一次系統用途,也比較不容易產生互相矛盾的架構假設。

第一版還缺少什麼?

今天的 Demo 使用明確規則讀取已知檔案,目的是先定義 Recon 產物與證據格式。

要處理任意 Repository,還需要加入:

  • 自動辨識語言、Framework 與 Entry Point。
  • 解析 Route、Event Handler、Queue Consumer 與 CLI Command。
  • 建立跨檔案 Call Graph 與 Data Flow。
  • 識別 Infrastructure as Code 與 CI/CD。
  • 對大型 Monorepo 分區 Recon。
  • 使用 Gemini 合併資訊,但限制它只能引用提供的證據。
  • 驗證輸出 Schema,拒絕不存在的檔案與行號。

其中「使用 Gemini 合併」不能取代程式證據。

比較安全的分工是:

Deterministic Tools
收集檔案、Symbol、設定、引用位置

Gemini
分類元件、解釋資料流、提出需要補查的問題

Validator
確認引用存在,並把無證據結論降級為 Inferred 或 Unknown

這樣能利用模型理解不同技術棧的能力,又不必相信它記住的每一個架構細節。

Recon 的完成條件

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

Application
用途、技術棧與主要 Entry Point 已確認。

Actors
外部使用者、服務與維運角色已列出。

Inputs
Network、Form、File、Environment 與 External Integration 已盤點。

Data Flows
重要輸入已追蹤到 Storage、Output 或危險 Sink。

Trust Boundaries
每條邊界都有資料、控制與證據位置。

Unknowns
Repository 外的必要資訊已列出取得方式。

如果專案包含多租戶、Plugin、管理後台、多個部署目標或複雜權限繼承,就不應強迫 Recon 在固定 Token 用完時結束。

正確做法是把複雜區域拆開調查,再合併成同一份架構模型。

今天的結論

AI 在找漏洞前,必須先知道它正在檢查什麼系統。

一份可用的 Recon 至少要回答:

  1. 應用程式如何啟動,使用哪些技術。
  2. 誰會與系統互動,各自預期能做什麼。
  3. 外部輸入從哪裡進入,又流向哪個 Sink。
  4. Authentication 與 Authorization 在哪一層執行。
  5. 資料跨越哪些信任邊界。
  6. 哪些重要資訊不在 Repository 中。

今天的 Demo 將這些內容輸出為帶有檔案、行號與原始片段的 architecture.json

Recon 不是替 Repository 寫一段摘要,而是替後續每個 Finding 建立共同、可核對的世界觀。

明天,我們會設計資安、隱私與 SRE 的檢查規則,讓 Agent 根據架構選擇適用規則,而不是對所有專案套用同一份 Checklist。

參考資料


上一篇
Day 17|只報真正能利用的問題:降低 AI 資安報告的誤報
系列文
從 Vibe Coding 到 Production:用 Google AI 打造上線守門員18
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言