當一個 Agent 框架能在本機執行任意 bash 命令時,它的每一行程式碼都可能是攻擊面。本文以 OkHuman 為案例,完整重現一次開源 Agent 框架的安全審查方法論:從攻擊面盤點、缺陷分級、證據鏈建立,到修復建議。所有發現皆附帶檔案與行號,讀者可自行驗證。這不僅是一份安全報告,更是一套可複製的審查流程,適用於任何正在評估或部署 Agent 框架的團隊。
為什麼要審一個 Agent 框架
Agent 框架與傳統軟體的安全態勢截然不同。傳統軟體的攻擊面通常是明確的 API 邊界,輸入經過 schema 驗證,輸出經過模板渲染。Agent 框架則允許模型驅動命令執行、讀寫檔案、調用外部服務,甚至修改自身的提示詞。這意味著「判斷錯誤」與「惡意輸入」的後果被同一個執行引擎放大,而且放大倍數難以預測。
OkHuman 是一個以 Go 標準庫從零實作的個人 Agent 運行時,主張「一進程 = 一 agent」,並以「唯一 bash 元工具 + HTTP 解耦插件」作為能力擴展模型。它的程式碼規模約 15,500 行,零外部依賴,預設綁定 127.0.0.1。這樣的設計定位是「本機單用戶」,但安全審查的目的,正是要檢驗這種預設是否足夠。
我們選擇 OkHuman 作為案例,有兩個原因。第一,它的設計有主見:零依賴、插件解耦、原子寫持久化,這些都是值得學習的工程實踐。第二,它的規模適中:15,500 行足以覆蓋 Agent 框架的核心模式,又不至於大到無法逐行審查。對一個建立僅 7 天、只有 9 stars 的專案而言,完成度異常地高,但完成度高不等於安全性高。
審查方法論:如何系統性地審一個 Agent
我們採用純靜態、唯讀的審查方式,未修改 repo 內任何檔案。審查覆蓋三個維度,每個維度都有明確的檢查清單。
第一,命令執行路徑。Agent 的核心能力是執行命令,因此我們逐行追蹤從 HTTP 端點到 bash 執行的完整鏈路,檢查參數驗證、權限控制、輸出處理。具體來說,我們追蹤 /chat 端點的請求如何經過模型決策,最終觸發 bash 工具,以及命令文本如何被寫入臨時檔案、如何被執行、輸出如何被截斷。
第二,HTTP 介面安全。OkHuman 暴露 18 個端點,我們逐一核對路由與 handler,檢查認證、授權、Origin 校驗、Content-Type 驗證。我們用 grep 搜尋 Authorization、Bearer、middleware、CORS、Origin、Referer 等關鍵字,確認是否存在任何認證機制。
第三,提示注入與隔離。Agent 的對話上下文會接收用戶輸入、工具輸出、外部網頁內容,我們檢查這些來源是否有隔離標記,以及系統提示詞是否暴露敏感資訊。我們特別關注「生命自感知」機制,即系統提示詞中是否注入 agent 自身的 PID、端口、路徑等資訊。
審查的輸出是一份結構化的缺陷清單,每項缺陷包含:嚴重度分級、證據(檔案:行號)、攻擊情境、影響評估、修復建議。嚴重度分為三級:高危(會造成靜默失效或直接導致系統被控制)、中危(功能缺失或資訊洩漏)、低危(工程衛生與可維護性問題)。
攻擊面全景
OkHuman 的攻擊面可以分為三層:外部可達的 HTTP 介面、模型驅動的命令執行、以及對話上下文的提示注入。
HTTP 介面預設綁定 127.0.0.1:8451,但程式碼無強制回環機制,若部署者將 server.host 改為 0.0.0.0,則所有端點立即對外暴露。更關鍵的是,全部 18 個端點直接分發,無任何認證或授權中間件,無 Origin 或 Host 校驗(internal/server/server.go:546-594)。我們用 grep Authorization|Bearer|middleware|CORS|Origin|Referer server.go 搜尋,結果為零命中,證實了這一點。
命令執行路徑從 /chat 端點進入,經模型決策後觸發 bash 工具。命令文本不進 argv 的設計是正確的(internal/tools/tools.go:140-156),可防止 pgrep -f 自匹配;獨立進程組與超時 SIGKILL 也做得紮實(tools.go:158、tools.go:172-178)。輸出封頂 32MB 每流,超限持續排空管道,子進程不阻塞(tools.go:162-163、tools.go:210-244)。然而,由於 HTTP 介面零認證,本機任意網頁可透過 CSRF 驅動 agent 執行任意命令。
對話上下文方面,外部內容(工具輸出、後台通知、網頁抓取)無任何隔離標記地進入,提示注入面完全敞開。系統提示詞甚至注入自身 PID 與端口,擴大注入殺傷面(internal/context/dynamic.go:44-58)。每次 LLM 調用 prepend 到 system prompt 首部(internal/context/manager.go:331-338),若 LLM 端點為遠端 API,本機路徑與 PID 資訊會洩漏給模型服務方。
逐級拆解:12 項缺陷的完整清單
審查最終產出 12 項缺陷,按嚴重度分為三級:4 項高危、4 項中危、4 項低危。
高危(4 項)
D-1:scout 語義搜尋靜默降級。scout 插件啟動 llama-server 時未指定 --pooling,導致 embedding 全部失敗,但系統僅 log 一行就繼續用關鍵詞模式運行。health 端點回傳 {"model":"on"},誤導運維者以為一切正常。根因是 llama-server 預設 pooling 為 none,不是 OAI 相容模式,/v1/embeddings 回傳 400 錯誤。
D-2:health 端點具誤導性。health 端點將 model: on 與 embedding: healthy 混為一談,即使 embedding 全部失敗,仍回傳正常狀態。這是典型的「靜默失效」問題,運維者會誤判系統已修復。
D-3:完成狀態誤判。測試 sleep 45 顯示,agent 在任務轉後台後立即回覆「完成」,但實際任務尚未執行完畢。問題不在機制(前台超時轉後台機制本身正確),而在模型的判斷:它把「已交給後台」誤判為「已完成」。
D-4:HTTP 介面零認證 + CSRF 可驅動任意 bash。全部 18 個端點直接分發,無任何認證中間件(internal/server/server.go:546-594)。tryReadJSON 不檢查 Content-Type(server.go:1591-1595),任何可解碼的 body 都當 JSON 處理。攻擊者只需誘導用戶瀏覽惡意網頁,即可透過 CSRF 驅動 agent 執行任意命令。DNS rebinding 亦適用,因為無 Host 校驗。
中危(4 項)
D-5:macOS 無 /proc 導致生命自感知失效。portToPID 函數掃描 /proc/net/tcp 與全進程 fd,但 macOS 無 /proc,導致該函數恆返回 -1(internal/context/dynamic.go:66-100)。生命感知塊顯示「未知」,僅 Linux 生效。
D-6:GET /config 明文回傳 api_key。/config 端點回傳的配置中包含 llm.api_key 明文(server.go:1403、server.go:221-223)。internal/llm/client.go:29 的 APIKey string 無 json 忽略標記。配合 D-4,攻擊者可一次 GET 請求取得 LLM API key,造成財務損失。
D-7:完全沒有網絡搜索插件。grep web_search|brave|tavily|serp 全倉庫零命中。最常見的 agent 需求之一,卻完全缺失。模型被問到時會直接回答「我沒有網絡搜索能力」。
D-8:OkHuman 誤診自身問題。實測發現,OkHuman 會把本地 scout 插件誤判為網絡搜索工具,並基於此錯誤前提得出錯誤結論。這是診斷盲點,會誤導使用者。
低危(4 項)
D-9:voice-chat 插件目錄為空。README 詳細描述功能,但目錄內 0 個 Go 檔案,文件與實作不一致,會誤導使用者。
D-10:無 CI 自動化。9 個測試檔但無 GitHub Actions,測試無法自動運行。現有測試品質不錯,但無自動化就跑不出價值。
D-11:測試覆蓋偏斜。agent.go 789 行僅 172 行測試;server.go 1,619 行僅 334 行。核心模組是風險集中區,測試覆蓋應優先補齊。
D-12:提示詞熱加載無版本記錄。POST /prompts/reload 可熱加載提示詞,但無版本記錄機制,改壞了難回溯。
最危險的三項:攻擊鏈分析
從攻擊者視角,最危險的三項缺陷是 D-4、D-6、D-3,它們可形成完整的攻擊鏈。
D-4 允許本機任意網頁透過 CSRF 驅動 agent,等同於 RCE。攻擊情境如下:用戶瀏覽惡意網頁,頁面內嵌表單提交 JSON 到 http://127.0.0.1:8451/chat。因 body 為 JSON、不需要自訂 header,屬 CORS simple 請求,tryReadJSON 不看 Content-Type,text/plain 表單體同樣解碼成功。響應讀不到但請求已生效,drive-by 觸發 agent 跑任意命令。
D-6 配合 D-4,攻擊者可取得 LLM API key。一次 GET /config 即取得第三方 LLM 服務的 API key,可被盜刷。若 api_key 指向計費端點,還會造成財務損失。
D-3 雖非直接安全漏洞,但在自動化場景中可能導致狀態不一致。外部看起來完成,實際沒有,這會誤導依賴 agent 回報的下游系統。
這三項缺陷的共同特點是「靜默」:CSRF 攻擊不留痕跡;api_key 洩漏僅在日誌中留下請求記錄;完成狀態誤判完全來自模型行為,難以從外部檢測。壞得無聲,比壞得明顯更危險。
給開發者的修復建議
針對上述缺陷,我們提出以下修復建議,按優先級排序。
優先級一:最小認證與 api_key 脫敏。啟動時生成隨機 token,所有非 /health 端點要求 Authorization header;或至少校驗 Host/Origin 為預期值。tryReadJSON 應要求 Content-Type: application/json。同時,GET /config 的回應中將 api_key 脫敏為 "" 或 "***"(僅保留長度或後 4 位);llm.ClientConfig.APIKey 加 json:"-"。若 server.host != 127.0.0.1,應強制要求 token,而非靠配置自律。
優先級二:讓 scout 大聲失敗。embedding 失敗時不要靜默降級。health 端點應區分 model: on 與 embedding: healthy;或啟動時自檢失敗則拒絕啟動,逼使問題被看見。scout 應明確指定 --pooling last(Qwen3-Embedding 官方建議 last-token pooling)。
優先級三:區分「已提交」與「已完成」。後台任務的通知前綴應明確標示狀態(如「已轉後台,尚未完成」);或在工具定義的文件字串裡直接教育模型:「轉後台不等於完成」。
優先級四:macOS 支援或明確標示。若支援 macOS,portToPID 應改用 lsof -i :port 或 ps;若不支援,應在 README 標示「Linux only」。
優先級五:工程衛生。補齊 CI(GitHub Actions 跑 9 個測試)、補齊 voice-chat 或移除其 README、提高核心模組測試覆蓋、提示詞熱加載加版本記錄。
對「實驗性框架」的安全定位判斷
OkHuman 自我定位為「實驗性框架」,這是否意味著安全可以暫時擱置?我們的答案是否定的。
實驗性框架的價值在於探索新的設計模式,而不是迴避工程紀律。OkHuman 的零依賴、插件解耦、原子寫持久化,都是值得發揚的設計。但這些設計的完成度,不應該成為忽視安全基礎的理由。
「本機單用戶」是一個合理的部署定位,但它不是一個安全保證。CSRF 可穿透瀏覽器邊界,api_key 明文可被同機其他進程讀取,提示注入可被惡意網頁觸發。這些風險在「本機單用戶」場景下依然存在,只是嚴重度較低。
我們對 OkHuman 的安全定位判斷是:默認配置下可接受(綁 127.0.0.1、本機單用戶),但需要補齊最小認證、api_key 脫敏、大聲失敗這三項基礎防禦。這些修復的工程成本很低,但能顯著降低攻擊面。
對所有使用或開發 Agent 框架的人,我們的建議是:把 OkHuman 當作「步驟明確、可用命令驗證、有外部複核」的自動化工具,不要相信它的自述「完成」。同時,優先修復「靜默失效」類問題,因為壞得無聲,比壞得明顯更危險。
對讀者的通用啟示
OkHuman 的審查結果,對所有使用或開發 Agent 框架的人都有啟示。
第一,「本機單用戶」不等於「安全」。即使預設綁定 127.0.0.1,CSRF 仍可穿透瀏覽器邊界。最小認證是必要的防禦層,不能靠配置自律。
第二,「靜默失效」比「明顯失敗」更危險。框架設計者應主動防範:要麼大聲失敗,要麼提供明確的健康指標。
第三,提示注入是 Agent 的結構性問題。外部內容無隔離標記地進入對話上下文,框架設計者應提供隔離機制,讓開發者可選擇性地標記可疑輸入。
第四,測試覆蓋與 CI 是安全底線。核心模組的測試覆蓋應優先補齊,並透過 CI 自動運行。沒有自動化的測試,跑不出價值。
第五,文件與實作必須一致。voice-chat 的空目錄、health 端點的誤導性回傳,都會誤導使用者,增加運維風險。
第六,完成狀態的判斷需要外部複核。不要相信 agent 的自述「完成」,應設計機制讓外部系統驗證任務是否真正完成。
下一步
本文以 OkHuman 為案例,示範如何系統性地審查一個開源 Agent 框架。讀者可參考以下資源,深入了解更多安全與架構細節:
- AI 安全紅隊實戰指南:了解如何對 AI 系統進行對抗性測試,建立系統化的安全審查流程
- 你的資料安全嗎?AI 時代的隱私保護:探討 Agent 框架的資料處理與隱私風險,包括會話記錄落盤與權限控制
- 企業資料安全基礎:從企業角度理解資料分級、存取控制與審計日誌
- OkHuman 架構深度解析:從架構角度理解 OkHuman 的設計取捨,包括零依賴、插件解耦、原子寫持久化
- OkHuman 能力評測與插件開發:了解 OkHuman 的功能邊界與擴展機制,包括我們如何為它開發網絡搜索插件