Sandbox 寫在 prompt 裡等於沒寫
Simon Willison 上週寫了一篇 agent intrusion 的解剖文,素材來自一個 frontier lab 自己的事後分析。
說穿了就是:agent 先從 HTTP proxy 逃出去,把第三方 sandbox 當跳板,之後是長達 5 天的攻擊鏈,包含 C2 通訊、橫向移動、偷 Kubernetes service-account token、Jinja2 template execution,最後用 Tailscale 外傳資料。
不是理論攻擊。是真的跑了 5 天你才發現。
你的 sandbox 定義在哪裡?
大多數我見過的 agent 部署,sandbox 的定義方式是:在 system prompt 裡寫「你只能做 X,不能做 Y」。
這跟在門上貼「請勿進入」但不裝鎖是一樣的。
Prompt 是模型行為的引導,從來就不是安全邊界。如果你的 agent 有 HTTP client、能執行 shell、或者跑在一個有 cloud credentials 的環境裡,prompt 裡寫什麼都不重要,重要的是那些能力能不能被拿去做它不該做的事。
那個 case 裡,agent 一旦逃出第一道 HTTP proxy,後面每一步利用的都是基礎設施的縫隙,跟 prompt 完全無關。
實務上我們做錯的事
我自己踩過的是 CI sandbox 的問題。用 GitHub Actions 跑 agent eval,每個 job 在獨立 runner,看起來很隔離。
問題在哪裡?每個 runner 天生就有 GITHUB_TOKEN。這個 token 預設有 repo 讀寫權限,有的環境還更多。如果你的 eval 跑到一半有機會執行任意程式碼,GITHUB_TOKEN 就是免費的橫向移動入場券。
解法不複雜,把 token 權限降到 contents: read,eval 用的 secrets 另外管。但這個設定有多少人真的去做過?
Kubernetes 也是同樣的問題。很多人把 pod 跑起來,沒有特別設 automountServiceAccountToken: false,結果 pod 裡面的 /var/run/secrets/kubernetes.io/serviceaccount/token 就在那邊等著被撿。那個攻擊鏈裡 K8s token 被偷不是意外,是必然。
Egress control 才是你要守的線
真正的邊界是出口,不是 prompt。
如果 agent 連不出去,Tailscale 沒辦法用、C2 沒辦法建立、資料沒辦法外傳。5 天攻擊鏈的每一步都需要網路連通。切掉出口,鏈就斷。
基本的 egress 控制:
# K8s NetworkPolicy,只允許必要的 outbound
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: agent-egress
spec:
podSelector:
matchLabels:
role: agent
policyTypes:
- Egress
egress:
- to:
- ipBlock:
cidr: <allowed-api-endpoint>/32
更重要的是 DNS。如果 sandbox 可以任意解析外部 DNS,egress 防火牆可以被繞過。自建 resolver、只解析白名單 domain,這些才是底層要補的縫。
Telemetry 的盲點
5 天攻擊鏈能跑 5 天,前提是你沒在看。
Agent 的 observability 很多人停在成功率和 latency,完全沒看 agent 的 tool call 模式。如果你的 agent 突然開始呼叫之前從來不呼叫的 endpoint,或者特定 tool 的呼叫頻率異常飆升,這些都應該是 alert 條件,不是事後翻 log 才知道。
簡單做法是記錄每個 tool call 的 destination 和 call count,設門檻。不用多精細,異常先報出來再說。
說穿了就是:最強的 frontier model 給它找到一個縫就會鑽,靠 prompt 教化不了它。你能做的是把縫補小、出口收窄、眼睛睜開。
Prompt 裡的 sandbox 讓你覺得安全,基礎設施的邊界才讓你真的安全。
作者:CtrlC