沙箱化工具執行
於隔離沙箱中執行 LLM 工具呼叫的逐步演練,涵蓋以容器為基礎的隔離、資源限制、網路限制與輸出清理。
當 LLM 呼叫工具——執行程式碼、查詢資料庫、呼叫 API——動作會在現實世界產生實際後果。若無沙箱化,劫持工具呼叫的提示詞注入可讀取敏感檔案、外洩資料或修改系統。沙箱化執行將工具呼叫隔離於受限環境中,即使工具呼叫為惡意的,損害仍可被局限。
步驟 1:設計沙箱架構
定義 SandboxConfig 資料類別,包含最大 CPU 秒數(預設 10 秒)、記憶體 MB(256)、磁碟 MB(100)、是否啟用網路(預設否)、允許網路主機清單、最大輸出位元組(10,000)、逾時秒數(30)、是否唯讀檔案系統(預設是)以及允許的命令清單。另定義 SandboxResult 結果類別,攜帶成功旗標、輸出、錯誤、退出碼、執行時間毫秒、資源使用字典、是否被強制終止與終止原因。
步驟 2:建置以 Docker 為基礎的沙箱
DockerSandbox 類別使用 python:3.11-slim 映像。execute_code(code) 方法將程式碼寫入暫存目錄,然後建構 docker run --rm 命令並加上下列旗標:依配置設定記憶體限制、--cpus=1、若未啟用網路則 --network=none、若唯讀則 --read-only、--tmpfs /tmp:size=50m 供可寫暫存、將腳本掛載為唯讀、切換工作目錄至 /sandbox、以 nobody 使用者執行、加上 --security-opt no-new-privileges 防止權限提升。以 subprocess.run 執行並擷取輸出,遇逾時回傳標記為 was_killed=True 的結果。
步驟 3:實作程式碼預先審查
CodePreScreener 維護封鎖樣式清單(以正則表達式表示):subprocess.run/call/Popen/check_output(子程序執行)、os.system/exec/spawn/popen(OS 命令執行)、shutil.rmtree/move(檔案系統修改)、socket.(網路 socket 存取)、requests.get/post/...(HTTP 請求)、urllib(URL 存取)、__import__((動態匯入)、eval(/exec((動態程式碼執行)、寫入模式的 open(...)、/etc/、/proc/、/sys/(系統檔案存取)。screen(code) 依序比對,第一個命中則回傳不允許與理由。
步驟 4:建置輸出清理
OutputSanitizer 維護敏感樣式替換表:將 /home/\w+ 替換為 [HOME_DIR]、IPv4 位址替換為 [IP_ADDR]、password|secret|token|key 後面的值替換為 [REDACTED_SECRET]、臨時目錄路徑替換為 [TEMP_DIR]。輸出超過 5000 字元時截斷並附加 ... [output truncated]。
步驟 5:整合至 LLM 工具管線
SandboxedToolExecutor 組合預審、沙箱與清理三個元件。execute(tool_name, code) 先呼叫預審器,若不允許則立即回傳封鎖原因;若允許則送入 Docker 沙箱執行,最後將 stdout 與 stderr 經過清理器後一併回傳。
步驟 6:部署為服務
以 FastAPI 建立 POST /execute 端點,接受 tool_name 與 code 欄位,回傳 success、output、error 與 execution_time_ms。以 uvicorn sandbox.api:app --port 8640 啟動。
步驟 7:測試沙箱隔離
撰寫測試:安全程式碼 print(2 + 2) 應被允許;import subprocess; subprocess.run(['ls']) 應被封鎖;import requests; requests.get('http://evil.com') 應被封鎖;輸出清理應將 /home/user/data.txt 與 192.168.1.1 都替換掉。
相關主題
- 以能力為基礎的存取控制 —— 工具權限控制
- 工具權限範圍 —— 代理工具的最小權限
- 監控代理工具呼叫 —— 工具執行的可觀測性
- 代理動作允許清單 —— 限制允許的動作
為何在沙箱化 LLM 產生的程式碼時,`--network=none` 很重要?