先寫憲法,再跑掃描器:一套讓非資安人員也能做的威脅建模

你是不是也有過這種感覺?

寫了一堆功能,覺得差不多了,於是請工程師或用 AI 跑一次資安掃描器(Scanner)。

然後螢幕上湧出幾百行報告:「缺少 Security Header」、「依賴庫有已知漏洞」、「密碼硬編碼」。你看得眼花撩亂,不知從何改起。最後為了趕上線,把那些聽起來很嚴重的修掉,那些聽起來像噪音的就暫緩。

結果上線後,一個不起眼的路由被繞過,整個資料庫被清空。

問題出在哪?不是掃描器不夠強,而是你跳過了最關鍵的一步:先寫憲法,再跑掃描器

這個觀念來自我開源的專案 threat-model-skill(GitHub:https://github.com/fishbob889/threat-model-skill )。它是一套給 Claude Code 用的指令集,專幫不會資安的開發者做威脅建模。它的核心邏輯只有一個:在跑工具之前,先用自然語言定義出「絕對不能被打破的規則」。

這篇文章會帶你走一遍這個過程。不講艱澀理論,只談怎麼用白話文保護你的系統。

一、資安底層:守護 CIA 三要素

在做任何檢查前,我們要先問自己:「我們到底怕什麼?」

業界有一套最基礎的分類法,叫 CIA 三要素。每一種資安事件,最後都能歸類到這三者之一:

  1. 機密性(Confidentiality):資料只讓該看的人看到。
    例子:資料庫裡的密碼、API 金鑰、客戶個資。
    失敗場景:你把金鑰寫在前端 JavaScript 裡,任何人按 F12 開開發者工具就能看到。

  2. 完整性(Integrity):資料和系統只能被授權的人、用授權的方式改動。
    例子:訂單金額、使用者角色、帳單。
    失敗場景:一個普通登入帳號,呼叫管理員 API 把自己升級成 admin,或是刪掉別人的資料。

  3. 可用性(Availability):該提供服務的時候要能提供。
    例子:網站能正常開啟、API 有回應、備份還原得回來。
    失敗場景:一個沒有限速的登入端點,被機器人攻擊幾萬次,導致網站癱瘓。

為什麼要先講 CIA?因為同樣的漏洞,破壞不同元素的代價完全不同。

掃描報告裡最常見的錯誤,就是把「DoS(影響可用性)」和「授權繞過(影響完整性/機密性)」混在一起排。結果,真正要命的漏洞被幾十條「缺個 Security Header」的小問題淹沒了,你以為沒事了,上線就被駭。

所以,做資安檢查的第一步,不是拿工具掃,而是確認每個漏洞「破壞的是 C、I 還是 A」。

二、五個核心問題:先寫憲法,再跑掃描器

這套方法來自 OpenAI 的 Codex Security plugin,我把它轉化為 Claude 可以執行的流程。順序很重要:先問清楚,再開掃。

問題 1:哪些東西被偷或被改會出大事?(重要資產)

把資產列成表:金鑰、密碼、含個資的資料表、簽章用的 secret,以及「對其他機器下命令的能力」本身。

從程式碼推:設定檔有哪些欄位?哪個 model 有加密欄位?哪個 table 有客戶資料?每一項寫下:放在哪、誰能讀、外洩或被改的後果。

問題 2:誰能碰哪些資料?(角色權限)

畫一張矩陣:匿名 / 一般使用者 / 管理員 / 機器帳號,各自可讀、可寫、可執行什麼。

重點是:不要看 README,要看程式碼裡每一條 API 路由實際掛的是哪個認證檢查。

這是我實戰中最常抓到的坑。人工盤點清單往往是「說法」(例如:README 說這是 admin-only),但測試才是「證據」。第一次用這套方法時,我們就發現一條漏洞:盤點說是 admin-only,但測試證明任何登入帳號都打得到那條路徑。

問題 3:哪一段是你管不到的?(信任邊界)

你控制不了的地方,就是黑客進來的地方。

這些地方包括:使用者輸入、第三方 API 回傳的東西、你 SSH 過去的其他機器回傳的輸出、瀏覽器。還有一個 2026 年新增的重點:AI agent 讀進來的任何內容(網頁、PR、貼上的文字、掃描報告)。Agent 讀到的文字可能含有指令,這是一種新的攻擊來源。每一條邊界要寫下:什麼東西穿過去、誰控制它、現在有什麼防線。

問題 4:哪些規則絕對不能被打破?(安全不變條件 Invariants)

這是整套方法的核心,也是 skill 名字裡「憲法」的意思。

句型固定為:「⟨對手⟩具備⟨能力⟩時,不能⟨結果⟩」。

範例:
– 「匿名請求不能讀到任何需登入的資料。」
– 「非管理員不能改變任何機器的狀態。」
– 「JWT 只接受 header,不接受 query string。」
– 「掃描目標只能是白名單內的主機。」

這裡有兩個規矩:
1. 只寫程式碼真的保證的。還沒保證的放到「已知弱點」那一節,不要假裝有。
2. 每一條要標「落實等級」:L1 只寫在文件裡、L3 有一支沒它就會 fail 的測試、L5 有 runtime 檢查或 hook 自動攔。還要寫「怎麼驗」——寫不出驗證方法的 invariant 是願望不是保證。

有了這份清單,掃描器的每一條輸出都要回答「它違反哪一條 invariant」。對不到的,不是這裡的 finding,登記後轉給負責的流程(依賴升級、反代設 header、限流),報告就不會被噪音淹掉。

問題 5:外面有哪些門是開著的?(攻擊入口)

列出所有入口:沒有認證的路由(登入頁本來就沒有)、監聽的 port、SSH、webhook、定時抓外部資料的排程。

最容易被忽略的一項:平台預設值

2026 年大部分 vibe coding(快速原型開發)出事的案例都不是程式碼寫錯,是預設值:專案預設公開、資料庫 row-level security 預設關、CORS 預設 *、debug 模式沒關。

三、五問之後:掃描、判讀、修補

當我們寫完「憲法」,就可以開始跑 skill 了。它分為三個階段:

  1. 兩層掃描
    – 先跑決定性工具:gitleaks 找機密、pip-audit / npm audit 找依賴漏洞、bandit 找程式碼壞味道。每個工具不是「跑了」就是明講「跳過」,不能讓「沒跑」看起來像「沒問題」。
    – 然後由 invariant 驅動語意層:對每一條 invariant 問「給我一個會違反它的請求」。

  2. 判讀
    每條 finding 拆成 Source(不可信輸入從哪進來)→ Control(路徑上該擋它的檢查,狀態是缺、被繞、範圍錯還是不完整)→ Sink(最後打到哪:執行命令、查資料庫、寫檔、另一台機器、機密)。路徑上有有效的 Control 就是誤報;沒有 Control 而且 Sink 打到資產,才是真的。嚴重度用固定的表算(影響 × 可達性),不靠感覺。

  3. 修補
    修在邊界(該有 Control 的地方),不是在 Sink 貼補丁;寫一支「修前會 fail、修後會 pass」的回歸測試,而且兩邊都真的跑一次;修完把 invariant 的等級往上升。

四、第一次實戰的結果

讓我們來看看實際效果。受試者是一套內部維運儀表板(FastAPI + Vue),透過 SSH 管理約四十台 Linux 機器,14 個 router、131 條路由。

只用了一個下午:
– 寫出 11 條 invariant。
– 9 條 finding 全部關閉,其中兩條是之前人工稽核漏掉的真實授權漏洞(任何登入帳號可對任一台機器封鎖或解封 IP;任何登入帳號可刪光整個文件庫)。
– 5 條 invariant 從「只寫在文件」升到「有測試」或「runtime 攔截」。
– 依賴漏洞 Python 47 條降到 4 條無修復版、npm 5 條清零。

同一套方法回頭用在專案自己的防護 hook 上,又找到四個繞過。

五、怎麼用?

如果你也想試試看這套方法,可以安裝我的 threat-model-skill

安裝步驟:

git clone https://github.com/fishbob889/threat-model-skill ~/.claude/skills/threat-model

使用方式:
在 Claude Code 裡,對著你的專案說:「這個專案上線前做一次威脅模型」。

Claude 會先從程式碼推出憲法草稿,停下來給你看,你改完它才開始掃。產出兩份檔案放在專案的 docs/security/:
1. THREAT_MODEL.md(憲法):你的安全規則清單。
2. findings.md(finding 帳本):掃描結果與修補建議。

總結來說:
資安不是跑個工具就結束的任務,而是一個定義邊界、驗證規則的過程。先寫憲法,再跑掃描器,能讓你的報告從噪音變為行動指南。

喜歡這篇文章的話,歡迎分享給身邊寫程式的朋友。

專案連結:https://github.com/fishbob889/threat-model-skill

發佈留言

這個網站採用 Akismet 服務減少垃圾留言。進一步了解 Akismet 如何處理網站訪客的留言資料