1991 年 1 月 7 日,有人試圖入侵 AT&T 貝爾實驗室的對外閘道。對方以為自己找到了 sendmail 一個有名的除錯漏洞,想藉此拿到密碼檔。
負責這台機器的 Bill Cheswick 沒有直接把人擋掉,他想看看對方到底要做什麼。他先寄了一份假的密碼檔過去,幾天後又坐在終端機前親手假扮系統,應付對方的指令。後來,他用 Unix 的 chroot 準備了一個隔離的環境:一個被圈起來的目錄,裡面擺了幾樣讓人誤以為是真機器的東西。Cheswick 自己也承認,這個環境並不太逼真。他把這個環境叫做 jail,也開玩笑地叫它蟑螂屋,接著花了好幾個月觀察這位被他稱為 Berferd 的入侵者。這段經歷後來寫成〈An Evening with Berferd〉,在 1992 年初的 USENIX 會議上發表。
Cheswick 在文章裡很坦白:他問過幾位 Unix 專家,結論是 chroot 並不完美。只是如果把編譯器和某些程式拿掉,裡面的人就很難逃出去。
chroot 本來不是為了安全設計的。它在 1979 年的 Unix 第七版就出現了,據說 1982 年 Bill Joy 把它加進 BSD,是為了測試安裝和編譯流程。後來它被拿去關入侵者,2000 年 FreeBSD 再把這個想法擴充成正式的 jail 功能,之後的容器技術也沿著同一條路走下去。
權限規則在指令執行前比對的是指令的字串:寫法符合某條規則就放行或擋下,換個寫法就可能對不上。jail 管的是程式實際上做得到什麼:不管指令寫成什麼樣子,碰不到的檔案就是碰不到,連不出去的網路就是連不出去,守著這道牆的是作業系統。
Claude Code 內建了這樣一道牆,包在它執行的 shell 指令外面。它預設是關的,在 session 裡打 /sandbox 就能打開設定面板。底層用的是作業系統本身的隔離機制:macOS 用系統內建、限制程式能碰哪些檔案和網路的 Seatbelt;Linux 和 WSL2 用 bubblewrap 這個工具建立隔離環境,另外還要裝 socat。原生的 Windows 和 WSL1 都沒有 sandbox。
打開之後,預設的範圍是這樣:指令只能寫入工作目錄、暫存目錄和另外加進來的目錄,其中幾個受保護的路徑仍然禁止寫入;網路沒有直接的出口,所有連線都要經過一個本機的 proxy,由它比對允許的網域,而這份清單一開始是空的。連到清單外的主機會怎樣,取決於權限模式:Manual 模式會跳出詢問,auto mode 和 dontAsk 會拒絕,bypassPermissions 則直接放行;ssh、資料庫用戶端這類不經過 proxy 的工具,就算主機在清單上也連不出去。
面板裡還可以選擇讓 sandbox 裡的指令自動執行,不再逐一詢問。就算這樣,明確禁止的規則依然有效,像 git push 這類被寫成每次都要確認的規則,也照樣會跳出確認。
類比到這裡要轉個彎。Cheswick 關的是外來的入侵者,他可以把編譯器整個拿掉,裡面的人做不了事也無所謂。agent 是自己請來幫忙的,它需要讀程式碼、跑測試、裝套件,sandbox 不可能把這些都清空。
所以設定 sandbox 的重點,在於分清楚哪些是 agent 工作需要的,哪些不是。程式碼、套件的下載來源要留下;憑證、跟這次任務無關的網域,就該拿掉。
最容易漏掉的是讀取。sandbox 預設擋的是寫入,官方文件寫得很清楚,裡面的指令預設能讀到機器上大部分的檔案,包括 ~/.ssh 和 ~/.aws/credentials 這類憑證;Claude Code 環境變數裡的機密,也會原封不動地傳進去。光把 sandbox 打開,憑證並沒有被保護。
網路那一側也一樣,允許清單上的網域,就是留在裡面的工具。官方文件特別警告,允許 github.com 這種範圍很大的網域,本身就可能成為外洩的管道。proxy 只看連線時填的主機名稱,不檢查加密的內容,裡面的程式可以用 domain fronting 這類技巧:表面上連到允許的網域,加密的內容裡其實要求同一個 CDN 上的另一個網站。
官方的結論是兩邊缺一不可。沒有網路隔離,被入侵的 agent 可以把 SSH 金鑰送出去;沒有檔案隔離,它可以改掉系統的設定,替自己打開網路的出口。
照這些限制,一個比較完整的起點大概是這樣。先用 /sandbox 打開 sandbox。接著在 sandbox.credentials 裡把 ~/.ssh、~/.aws/credentials 這類檔案設成禁止讀取,並列出要在指令執行前移除的環境變數,例如各種 token。這只對 sandbox 裡的指令有效,hooks、MCP server 這些牆外的程式照樣拿得到,官方文件另外提供了 CLAUDE_CODE_SUBPROCESS_ENV_SCRUB 這個設定處理這部分。sandbox 的禁止讀取管不到 Claude 自己的 Read 工具,所以權限規則裡也要對同樣的路徑加上 Read 的禁止規則。網路的允許清單只放這個專案真正需要的網域,例如套件庫,避開範圍太大的網域。
最後是兩個安靜的漏洞。sandbox 啟動不了的時候,例如 Linux 上少了 bubblewrap,或是平台本身不支援,Claude Code 預設不會報錯,而是直接在沒有 sandbox 的情況下照跑;設定 failIfUnavailable 之後,它才會拒絕啟動。另一個是逃生口,下一節會談;在 /sandbox 面板的 Overrides 分頁打開 Strict sandbox mode,就能關掉失敗後在牆外重跑這條路。但被列進 excludedCommands 的指令,以及使用者自己在 ! 提示下打的指令,仍然在牆外執行。
sandbox 包住的只有 shell 指令和它們啟動的程式。Claude 用來讀寫檔案的工具、WebFetch、WebSearch 不在裡面,它們照的是權限規則;hooks 和本機的 MCP server 也在外面,用的是使用者完整的權限。
刻意留下的那個逃生口,是給在 sandbox 裡跑不起來的工具用的。指令失敗時,Claude 可以要求在 sandbox 外面重跑一次,誰來核准則取決於當下的設定:Manual 和 acceptEdits 模式會跳出確認,auto mode 交給分類模型判斷,dontAsk 模式直接拒絕,bypassPermissions 模式完全不問;如果那個指令本來就符合某條允許規則,重跑也不會跳出確認。
想把所有東西都關進同一道牆裡,就要把 Claude Code 整個放進隔離環境執行,例如 dev container、虛擬機器、雲端 session,或是 Claude Code 底層用的那套開源 sandbox runtime。
Cheswick 在 1991 年用 chroot 關住一個不可信的入侵者。他知道那道牆不完美,所以花心思決定裡面要留下哪些東西,再坐在外面盯著看。
AI agent 讓這個老問題換了對象。這一次要關的,是自己請來幫忙、卻可能被別人騙去做壞事的 agent,牆裡面還得留夠它工作用的工具。牆蓋好之後,該回頭檢查的事跟當年一樣:留在裡面的,有沒有不該留的鑰匙。
延伸閱讀