我們如何建立沙盒化的 Claude Agent 而不洩漏其 API Keys
現在大家都在建立 Agent,但大多數的框架都是為「本地優先」的世界而設計。它們假設操作鍵盤的人就是 API keys 的擁有者。
在生產環境中,這個假設會成為一個隱患。
如果你給一個 Agent 一個 bash shell 和不受信任的輸入,你實際上是為終端使用者打開了一個查看你環境變數的窗口。這會迅速將一個令人興奮的 Agent 變成一場安全惡夢。如果你夠幸運,他們在沙盒中只會得到你的 API key,然後你醒來時發現 Anthropic 的帳單高得嚇人。如果 Agent 運行在你的基礎設施上,惡意行為者可能會做更糟糕的事情,執行可能嚴重擾亂甚至破壞你部分營運的程式。
就在上週,Claude Agent SDK(可以說是目前最熱門的人工智慧基礎設施)獲得了 300 萬次下載。鑑於其受歡迎程度,我不禁想知道有多少公司正在花時間採取正確的安全措施來避免這類災難。
為了交付安全的產品,我們必須超越「本地優先」的假設。我們建立了一種架構,讓我們的 Agent 甚至不知道自己的 API keys。我們必須停止與 SDK 對抗,並開始「欺騙」它。
方法如下。
事情的開端:意外成為 PPTX 專家
在 Listen Labs,我們在 PowerPoint 的苦海中花費了比我們願意承認的更多時間。我們已經建立了自己的投影片產生器(是的,這在技術上是我們第二篇關於 PPTX 產生功能的工程部落格文章)。
這在辦公室裡有點像個老梗笑話,但所有花在製作投影片上的時間意味著我們了解投影片產生的每一個細節和 XML 的邊緣情況。所以我們的下一個挑戰是給一個人工智慧一個檔案系統、bash、一些專門的程式庫、一個 PowerPoint 技能,然後讓它自由發揮。
在評估了現有情況後,我們決定基於 Claude Agent SDK 進行開發。Claude 在檔案系統中處理複雜、多步驟的 Agent 任務方面具有獨特的可靠性。我們親眼見證了 Claude 如何有效處理高擬真度的檔案產生,而且該 SDK 已經提供了原生的 PowerPoint 技能,為我們提供了完美的基礎。
透過利用 Anthropic 在終端機互動、檔案編輯邏輯以及子 Agent 編排和平行化方面的工作,我們可以省去建立檔案系統原生 Agent 迴圈的繁重工作,轉而專注於推理部分。
SDK 安全性尚未準備好迎接開放網路
人們可能會認為像 Claude SDK 這樣的工具會開箱即用地處理自身的安全性。合理地假設會有一種內建機制來沙盒化環境變數,將它們與 Agent 的程式執行過程隔離開來。
但事實並非如此。
這是因為這些 SDK 最初是為本地開發而設計的;它們直接從 process.env 讀取 ANTHROPIC_API_KEY。當你給一個 Agent 一個 Bash shell 時,你已經為你的環境留下了一個敞開的窗口。一個簡單的 prompt 注入 echo $ANTHROPIC_API_KEY,你的憑證就洩漏了。
然而,威脅不僅僅是變數洩漏。因為 Agent 作為原生程序運行,它不僅僅是「運行在」你的伺服器上;它就是你的伺服器。如果沒有適當的隔離,你的 Agent 就會成為潛在的殭屍網路節點,進行外部網路呼叫或運行 fork bomb,導致你的基礎設施癱瘓。
你可以告訴 Agent:「如果你洩漏這個 key,我的奶奶就會死」,但使用者上傳檔案中的一個巧妙的 payload 可以在瞬間覆蓋這個指令。
我們為何拒絕「協調器模型」
當我們尋找生產模式時,我們發現文獻中存在一個奇怪的空白。我們看到最常見的方法是我們稱之為「協調器模型」(Orchestrator Model):將 Agent 迴圈保留在自己的基礎設施上,並將單個工具呼叫代理到一個獨立的沙盒中。
我們拒絕這種做法有 3 個主要原因:
原生相容性:Claude SDK 在程序內執行工具。要代理它們,你必須禁用內建工具並手動重新實現它們,每次更新都與 SDK 對抗。這對於子 Agent 來說尤其棘手,Claude Agent 使用子 Agent 來平行化任務或對新任務進行「重新審視」。為什麼要增加大量的開銷來重新建立 SDK 已經優雅處理的巢狀迴圈和狀態管理呢?
簡單部署:將 Agent 迴圈與執行分離——在我們的基礎設施上運行「大腦」並將工具呼叫代理到沙盒中——會使部署變得更加複雜。每次運行都需要持久化狀態並支援重播和恢復,而不是簡單地持久化一個容器 ID 並從上次停止的地方繼續。
延遲:透過在沙盒外部運行 Agent,所有檔案讀取、bash 命令等都需要網路呼叫。每個額外的網路請求大約只需要 100 毫秒,但單一任務的數百個工具呼叫會迅速累積起來。
為了讓 SDK「安全」而移除其功能,感覺是錯誤的權衡。因此,我們沒有改變 Agent,而是改變了它的環境。我們沒有採用協調器模型,而是建立了一種我們稱之為「工作站架構」(Workstation Architecture)的模型。
我們的「工作站架構」
我們決定停止與 SDK 對抗,並開始欺騙它。畢竟,它無法洩漏它不知道的東西。我們建立了一個環境,讓 SDK 可以執行複雜的 bash 命令和編輯檔案,同時完全不知道自己的 API keys。
步驟 1:沙盒 (E2B)
Agent 在每次運行時都存在於一個全新的、隔離的 VM 中。它可以執行 rm -rf / 或安裝惡意軟體;這是一個可拋棄的世界,所以沒關係。雖然 Anthropic SDK 包含內部安全掛鉤,但沙盒是主要的防線。
為了讓沙盒快速啟動,我們預先安裝了從 LibreOffice 和 markitdown 到專門的 Node 和 Python 程式庫的所有東西。
步驟 2:API Key 幻象
即使在沙盒內部,環境變數仍然是一個隱患,因為它們可以透過 prompt 注入被終端使用者存取。我們透過將 API key 儲存卸載到代理伺服器來解決這個問題。
我們給 SDK 一個虛擬 key,並將其 ANTHROPIC_BASE_URL 重新路由到我們的代理伺服器。當 Agent 發出呼叫時,代理伺服器執行「Key-Swap」:它在轉發請求之前剝離虛擬 key 並注入真實的 ANTHROPIC_API_KEY。Agent 保持完全自主,但在技術上不包含任何秘密。
import { Sandbox, ALL_TRAFFIC } from "e2b";
const sandbox = await Sandbox.create(templateId, {
timeoutMs: 45 * 60 * 1000,
envs: {
ANTHROPIC_API_KEY: "sk-ant-proxy",
ANTHROPIC_BASE_URL: `https://our-proxy.fly.dev/${hmacToken}`,
},
network: {
denyOut: [ALL_TRAFFIC],
allowOut: [proxyHostname],
},
});
// A simplified look at the proxy logic on Fly.io
function forwardToAnthropic(req, res, targetPath) {
const headers = { ...req.headers };
// THE SWAP: Strip the dummy and inject the real secret
headers["x-api-key"] = process.env.ANTHROPIC_API_KEY;
// Forward the request to Anthropic and stream the response back
const upstream = https.request(
`https://api.anthropic.com/${targetPath}`,
{
method: req.method,
headers,
},
(upstreamRes) => {
res.writeHead(upstreamRes.statusCode, upstreamRes.headers);
upstreamRes.pipe(res);
}
);
req.pipe(upstream);
}
步驟 3:保護代理伺服器
如果一個交換 key 的代理伺服器對網際網路開放,那將是一個巨大的隱患。任何人都可以利用它來增加你的 Anthropic 帳單。為了防止這種情況,我們使用會話範圍的 HMAC token 來授權每個請求。
在沙盒啟動之前,我們的後端會產生一個用秘密 key 簽名的 token。這個 token 與該特定的 45 分鐘時間窗綁定。由於 Claude SDK 允許自訂 baseURL 但不提供注入自訂 headers 的介面,我們將此 token 直接嵌入到 URL 路徑中。這使得代理伺服器成為一個即插即用的替代品,無需修改 SDK 的內部網路邏輯。
代理伺服器在交換之前驗證會話:
const { payload, signature } = parseToken(req.url);
// 1. Check signature (timing-safe comparison against our secret)
// 2. Check expiration (ensure the 45-minute window hasn't closed)
// 3. IP pinning (verify request IP matches the sandbox IP in the auth token)
if (
isValid(signature) &&
!isExpired(payload) &&
isAuthorizedIP(req, payload)
) {
// Note: We use this same pattern to securely route LangSmith
// tracing from the sandbox without exposing those keys either.
return forwardRequest(payload.targetProvider, req);
}
真實世界陷阱:基礎設施不喜歡 Agent 流量。我們最初將其託管在 Render 上,但他們的 WAF 立即阻止了我們的流量——一個 Agent 發送充滿 bash 腳本的 POST 主體看起來就像一個遠端程式執行(RCE)攻擊。我們轉移到更輕量級的 Fly.io,在那裡我們可以在沒有自己的防火牆扼殺 Agent 迴圈的情況下運行。
最終架構

結果:透過將身份驗證卸載到安全的代理伺服器,並將整個 Agent 保留在沙盒中,我們在確保安全的同時保留了 Claude SDK 的原生能力。即使 Agent 透過 prompt 注入被完全入侵,也沒有憑證可竊取,沒有網路可探查,也沒有基礎設施可破壞。
大家都在建立 Agent — 但我們仍處於早期階段。
Anthropic SDK 的巨大受歡迎程度證明了人們對 Agent 的渴望。但我們的經驗表明,本地演示與生產就緒服務之間的距離比大多數人想像的要大。
超越「受信任使用者」模型需要大量的基礎設施重擔,而這尚未成為標準人工智慧策略的一部分。在建立這個架構之後,我懷疑很少有組織正在建立準備好應對開放網路現實的 Agent。
我們仍在摸索專業的「Agent Stack」究竟是什麼樣子。這不總是阻力最小的路徑,但如果我們希望我們的 Agent 在現實世界中做有意義的工作,這項工作就必須完成。
如果你對這類基礎設施難題感興趣,歡迎加入我們!
