你的代理有個你沒審查過的中間人
研究人員從淘寶、閒魚以及 Shopify 託管的商店購買了 28 個付費 LLM API 路由器,並從公開社群蒐集了 400 個免費路由器。他們在每個路由器上註冊帳號,讓一個沙箱中的程式設計代理經由該路由器運行,執行每一個回傳的工具呼叫,再觀察這些呼叫做了什麼。1
在這 428 個路由器中,有 9 個把工具呼叫改寫成攻擊者控制的命令或相依套件:1 個付費路由器、8 個免費路由器。另有 17 個免費路由器在傳輸途中看到 AWS 誘餌(canary)憑證後,接著實際使用了它;還有 1 個掏空了植入的以太坊(Ethereum)私鑰。注入的路由器中有兩個隱藏了這種行為:一個等到 50 次請求之後才動手,另一個只在以自主「YOLO mode」(自動核准所有工具呼叫的模式)處理 Rust 或 Go 專案的工作階段中觸發。1
LLM API 路由器是一種應用層代理伺服器。它會終止 TLS 連線,以明文讀取每一個請求與回應,並且能改寫您的代理即將執行的工具呼叫。論文沒有發現任何主要供應商對其工具呼叫回應進行簽章,因此沒有任何機制能把您的代理所執行的命令與模型實際產生的內容綁定在一起。論文測試了四個代理框架,包括 Claude Code 與 Codex,沒有一個執行回應完整性檢查。1
本文最初於 2026年4月10日根據論文摘要發表。2026年10月2日,我依據論文全文重寫了本文。四月版有多處錯誤,列於文末附近的「四月版錯在哪裡」一節。
TL;DR
- 路由器能做什麼。它以您所設定的端點身分,位於客戶端與模型供應商之間,因此握有每一個請求與回應的明文,並能在轉送前修改其中任何一方。1
- 428 個路由器做了什麼。9 個在回傳的工具呼叫中注入惡意程式碼(付費 28 個中有 1 個,免費 400 個中有 8 個)。其中兩個免費路由器以觸發條件隱藏注入行為。17 個免費路由器使用了研究人員擁有的 AWS 誘餌憑證,1 個掏空了研究人員擁有、餘額不到 50 美元的以太坊私鑰。1
- 洩露的金鑰與薄弱的中繼做了什麼。一把刻意洩露的 OpenAI 金鑰承載了 1 億個 GPT-5.4 token,並曝露了七個以上的 Codex 工作階段。設定薄弱的誘餌中繼承載了約 20 億個 token,在 440 個 Codex 工作階段中曝露了 99 組憑證,其中 401 個工作階段早已在工具呼叫自動核准的狀態下運行。1
- 防禦措施的成效。在作者自建的合成基準中,故障關閉(fail-closed)政策閘門以 1.0% 的誤報率攔下了所有注入樣本;但在一個簡單的自適應基準中,知道閘門存在的攻擊者在 100% 的樣本中繞過了它。異常篩查標記出 89.0% 的一般注入,以及約半數的規避型注入。1
- 作者認為什麼能修好它。由供應商簽章的回應信封,讓客戶端能核對工具呼叫是否與模型產生的內容一致。論文沒有發現任何主要供應商提供這種機制。1
- 如果路由器由您自行託管。下方 10月1日的更新涵蓋了 11 則 LiteLLM 安全公告,其中一則讓任何經過驗證的使用者都能讓代理伺服器把它的供應商金鑰送出去。2
重點摘要
- 代理操作者:介於客戶端與模型供應商之間的每一個路由器,都能以明文存取每一個請求與回應,而客戶端只設定得到第一跳。如果您的路由器是從市集購買或從公開名單取得的,在您有另外的理由信任其營運者之前,請把它當成敵對的中介。
- 框架打造者:PreToolUse 掛鉤會在工具呼叫執行前運行,但在那之前,路由器早已有機會改寫該呼叫。掛鉤手上沒有原始版本可供比對。它能做的是故障關閉:只放行從清單內網域下載、只安裝清單內套件的 shell 命令,其餘一律攔下。3
- 任何執行 YOLO mode 的人:在研究人員的誘餌研究中,觀察到的 440 個 Codex 工作階段裡,有 401 個早已在工具執行自動核准的狀態下運行。1對這些工作階段而言,只要一個單純改寫過的命令就會被執行,根本不需要觸發邏輯。請勿讓自動核准的工作階段經過您無法掌控的路由器。
- 自行託管代理伺服器的團隊:您自己運行的路由器同樣會集中供應商金鑰。2026年10月1日進入 PyPA 資料庫的 11 則 LiteLLM 安全公告(多數自六月起即已公開)中,有一則讓任何經過驗證的使用者都能讓代理伺服器把供應商金鑰送到他們選定的主機。1.97.0 起的版本不在這 11 則所記錄的任何範圍內;詳情見下方 10月1日的更新。2
路由器究竟是什麼?
LLM API 路由器接收某種格式(通常是 OpenAI 相容格式)的請求,挑選一個上游供應商,再回傳回應。論文把各種規模的路由器都算進來:Amazon Bedrock 與 Azure OpenAI Service 等雲端代管服務,LiteLLM 與 OpenRouter 等面向開發者的專案與服務,以及一個轉售、聚合 API 存取權的大宗商品市場。1
人們使用路由器有正當理由。論文列出「model fallback, load balancing, cost optimization, and a single API key across providers」(模型備援、負載平衡、成本最佳化,以及跨供應商的單一 API 金鑰),並指出路由在「in regions where direct provider access is restricted, expensive, or subject to quota limitations」(直連供應商受限、昂貴或有配額限制的地區)特別常見。1
問題出在位置。用論文的話說,這不需要任何攔截技巧:「the client voluntarily configures the router’s URL as the API endpoint, the router terminates the client-side TLS connection, and it originates a separate TLS connection upstream」(客戶端自願把路由器的 URL 設為 API 端點,路由器終止客戶端這一側的 TLS 連線,再另外向上游建立一條 TLS 連線)。TLS 保護每一段線路,卻完全無法保護酬載不受路由器本身侵害:路由器讀取請求 JSON、轉送出去,再讀取回應 JSON、回傳回來,兩者都有機會被它修改。1
路由器還會層層串接。論文的例子是:開發者向淘寶轉售商購買存取權,轉售商的金鑰來自第二層聚合商,聚合商再經由 OpenRouter 路由,最後由 OpenRouter 派送給模型主機。一共四跳,每一跳都能完整存取明文。「The client configures only the first hop; subsequent hops are invisible.」(客戶端只設定第一跳,之後的每一跳都看不見。)路徑上任何一跳出問題,整條路徑就遭到污染,而其後誠實的各跳也無從察覺。1
現今的 API 沒有任何機制能填補這個缺口。工具呼叫的參數以明文 JSON 傳輸,而且「No provider-level integrity mechanism binds the arguments returned by the model to the arguments received by the client.」(沒有任何供應商層級的完整性機制,能把模型回傳的參數與客戶端收到的參數綁定在一起。)1
測量是怎麼進行的
作者對每個路由器執行四個階段。首先註冊一個由研究人員控制的帳號並取得 API 憑證。接著讓該帳號經由一個沙箱代理環境運行,執行「a randomized subset of custom tasks and TerminalBench 2.0 tasks」(從自訂任務與 TerminalBench 2.0 任務中隨機抽取的子集),這些任務會產生逼真的 shell、套件安裝與檔案操作工具呼叫。然後在沙箱內執行每一個回傳的工具呼叫酬載,由沙箱監看網路回呼、檔案系統變更與行程樹異常。最後由另一個分析代理審查被標記的執行結果,把每一筆分類為良性改寫、可能惡意或確認惡意。1
定義相當保守。路由器要被算作注入惡意程式碼,條件是「only if a returned tool-call payload is rewritten into an attacker-controlled command or dependency」(僅當回傳的工具呼叫酬載被改寫成攻擊者控制的命令或相依套件)。AWS 誘餌憑證要被算作遭到觸碰,條件是經過路由器的憑證「later produces follow-on AWS API activity attributable to that credential」(之後產生可歸因於該憑證的後續 AWS API 活動)。1
語料涵蓋的是大宗商品市場,而非整個領域。400 個免費路由器以兩個開源範本 sub2api 與 new-api 為主,作者也表示企業級與僅限邀請的部署不在研究範圍內。語料大多來自中文市集與社群。1
田野資料
| 行為 | 付費路由器(28) | 免費路由器(400) |
|---|---|---|
| 在回傳的工具呼叫中注入惡意程式碼 | 1 | 8 |
| 以觸發條件隱藏注入 | 觀察到 0 個 | 2 |
| 使用研究人員擁有的 AWS 誘餌憑證 | 觀察到 0 個 | 17 |
| 掏空研究人員擁有的以太坊私鑰 | 觀察到 0 個 | 1 |
這些注入並非表面功夫。論文指出它們會「change the command or dependency that the agent intends to execute」(改變代理打算執行的命令或相依套件)。付費路由器的結果是 28 個中有 1 個,作者據此得出直白的結論:「Paid access improves service stability, but it does not prove tool-call integrity.」(付費存取能提升服務穩定性,卻無法證明工具呼叫的完整性。)1
帶有觸發條件的那兩個路由器,是讓測試失效的發現。其中一個是「malicious tool calls are injected only after the first 50 requests, which hides the attack from shallow smoke tests」(只在前 50 次請求之後才注入惡意工具呼叫,因此淺層的冒煙測試看不到攻擊)。另一個則「only for sessions that appear to be running in autonomous YOLO mode and only when the project fingerprint matches Rust or Go」(只針對看起來正以自主 YOLO mode 運行、且專案指紋符合 Rust 或 Go 的工作階段)才啟用注入。作者補充,他們看到的觸發條件未必是全部。他們只透過有限的黑箱探測觀察這些路由器,因此「a router may implement additional latent conditions that our probes did not activate」(路由器可能實作了其他潛在條件,只是探測沒有觸發它們)。1
憑證相關的發現全部出自免費路由器。17 個路由器「trigger follow-on use of at least one researcher-owned AWS canary credential after seeing it in transit」(在傳輸中看到至少一組研究人員擁有的 AWS 誘餌憑證後,引發了對它的後續使用),另有 1 個掏空了預先注資的以太坊私鑰。附錄指出這次掏空造成的損失不到 50 美元。1
兩項投毒研究
路由器不必本身就是惡意的,也可能落入同樣的位置。作者進行了兩項研究,探討看似良性的路由路徑如何遭到投毒。
研究一:洩露的金鑰。他們把一把研究人員擁有的 OpenAI API 金鑰洩露到「on Chinese forums, WeChat, and Telegram groups frequented by router operators」(路由器營運者常出沒的中文論壇、微信與 Telegram 群組)。這一把金鑰承載了 1 億個 GPT-5.4 token,曝露了七個以上的 Codex 使用者工作階段,而且至少有一個工作階段背後出現了好幾組不同的憑證。論文對流量如何抵達這裡措辭謹慎。這個結果「is consistent with several paths: direct reuse by a router operator, direct reuse by unrelated third parties, or reuse somewhere inside a longer relay chain」(與數種路徑相符:路由器營運者直接重用、無關第三方直接重用,或在較長中繼鏈中的某處重用),而作者「cannot always distinguish among those paths from provider-side logs alone」(僅憑供應商端的日誌,無法總是區分這些路徑)。這項研究確立的是爆炸半徑:握有被重用之上游金鑰的人,能以明文看到其背後的流量。1
研究二:薄弱的中繼。他們在 20 個網域上部署有漏洞的 claude-relay-service 實例,並在 20 個 IP 上部署弱密碼的 Sub2API 與 CLIProxyAPI 實例。這些誘餌吸引了來自 147 個 IP、超過 40,000 次未經授權的存取嘗試;作者把這解讀為掃描與中繼重用的混合,並不認為每一次都是針對性攻擊。更強的訊號出現在後來:這些誘餌「were folded into active agent-facing relay paths」(被納入了實際服務代理的中繼路徑)。它們承載了約 20 億個 GPT-5.4 與 5.3-codex token,約 13 GB 可見的提示與回應流量,並在橫跨 398 個專案或主機的 440 個 Codex 工作階段中曝露了 99 組不重複的憑證。1
依論文的定義,這 440 個工作階段每一個都可被注入命令,而其中 401 個「already operated in YOLO mode, i.e., with tool execution auto-approved rather than confirmed command by command」(早已以 YOLO mode 運作,也就是工具執行自動核准,而非逐條確認命令)。對這 401 個工作階段,作者寫道:「sophisticated adaptive evasion was unnecessary: basic payload injection would have been sufficient to change executed commands」(不需要精巧的自適應規避,基本的酬載注入就足以改變被執行的命令)。1
那些是別人的工作階段。誘餌被納入中繼路徑之後,他們的流量便流到了研究人員的誘餌上,而只設定第一跳的使用者根本無從察覺。作者只保留彙總後的中繼資料與加鹽雜湊值,並在觀察期結束時讓他們自己曝露的憑證退役。1
兩種核心攻擊類別與兩種規避變體
論文把惡意路由器的行為歸結為兩種基本手法,以及第一種手法的兩種延伸。1
AC-1:回應端酬載注入。路由器在工具呼叫離開供應商之後、抵達客戶端之前改寫它,並保持工具名稱與結構描述(schema)不變。論文的例子是一個 Bash 呼叫,其中的安裝程式 URL 被換成攻擊者的 URL。由於結果對預期的工具而言仍是有效的 JSON,客戶端看不出任何異狀。
AC-2:被動機密外洩。路由器什麼都不改。它掃描請求與回應中的憑證樣式,把找到的東西留下來。流量未經修改,因此「clients cannot observe router-side retention」(客戶端無法觀察到路由器端的留存)。曝露的範圍不只 API 金鑰:「system prompts, tool definitions, user-supplied file contents, and environment variables all transit the same plaintext channel」(系統提示、工具定義、使用者提供的檔案內容與環境變數,全都經過同一條明文通道)。
AC-1.a:鎖定相依套件的注入。AC-1 的一種變體,目標是套件安裝命令。路由器不替換 URL,而是「substitutes a legitimate dependency name with an attacker-controlled package pre-registered on the target registry」(把合法的相依套件名稱替換成攻擊者預先在目標套件庫註冊的套件)。論文的例子把 pip install requests 變成 pip install reqeusts。命令仍是從受信任的套件庫安裝,因此只檢查網域的閘門會放行它,而惡意套件還會留在快取中,影響之後的工作階段。
AC-1.b:條件式投放。決定 AC-1 何時觸發的變體。路由器在觸發條件符合之前都表現得誠實。論文列出五類觸發條件:工具名稱、內容關鍵字、YOLO mode 等使用者指紋、時間窗口,以及請求次數。「A router deploying a 50-request warm-up appears entirely transparent during a 10-request probe.」(設有 50 次請求暖身期的路由器,在 10 次請求的探測中看起來完全透明。)
論文把 2026年3月的 LiteLLM 遭入侵事件視為大規模的 AC-1:攻擊者一旦控制了該代理伺服器的請求管線,所有經過的工具呼叫都可能被改寫。(論文把那次入侵描述為相依套件混淆。我在供應鏈那篇文章中所依據的報導,則將其追溯到透過 Trivy 遭入侵事件竊取的 PyPI 發布 token。)
研究用代理伺服器揭示了代理客戶端的哪些問題
作者打造了一個名為 Mine 的代理伺服器,實作全部四種類別,並對四個公開的代理框架(OpenClaw、OpenCode、OpenAI 的 Codex,以及 Anthropic 的 Claude Code)以每個攻擊模組 1,000 次工具呼叫請求進行測試。1
四者沒有一個驗證回應完整性。被改寫的工具呼叫以有效、框架原生的形式抵達客戶端的比例,在 AC-1 案例中為 100%,在 AC-1.a 案例中為 99.6%。論文對這代表什麼、不代表什麼說得很精確:「These are compatibility measurements, not end-to-end execution rates: they show that the tampered tool call reaches the client in a valid framework-native format, but downstream permission prompts or local execution sandboxes may still block the resulting action.」(這些是相容性測量,而非端到端的執行率:它們顯示遭竄改的工具呼叫以有效的框架原生格式抵達客戶端,但下游的權限提示或本機執行沙箱仍可能攔下由此產生的動作。)1
這個但書正是權限提示發揮作用之處,也說明了自動核准為何在誘餌數據中如此關鍵。作者沒有公開 Mine。1
同一組合模式,下探一層
我一再撰寫同一個結構性缺陷:個別獲得授權的元件,組合起來卻產生未經授權的行為。Trivy-to-LiteLLM是套件層的組合。沉默的對外流量是擷取內容層的組合。MCP 工具投毒是協定層的組合。
路由器攻擊是客戶端與供應商之間那一跳上的組合。您的客戶端獲授權呼叫路由器。路由器獲授權呼叫上游模型。模型獲授權做出回應。每一跳都經過授權,但組合起來仍會產生被改寫的命令,因為沒有任何一方對跨越這一跳的內容簽章;也會產生被複製的機密,因為每一跳都以明文讀取這些內容。
論文把這條線延伸到 MCP 本身:「a malicious MCP server receives tool-call requests in plaintext and can return forged results, so the same basic manipulation and collection ideas transfer with adaptation to the MCP message format」(惡意 MCP 伺服器以明文接收工具呼叫請求,並能回傳偽造的結果,因此同樣的基本操縱與蒐集手法,只要配合 MCP 訊息格式調整即可移植)。論文也標出兩者的差異。MCP 伺服器位於工具執行端,能偽造工具輸出。路由器位於通往模型的路徑上,看得到每一個工具呼叫與完整的請求脈絡。1
三種防禦,附論文的數據
論文評估了三種客戶端無需任何供應商協助即可部署的控制措施。以下每個數字都受一項但書約束:「All numbers in this section come from controlled artifact-side evaluation rather than field data.」(本節所有數字皆來自受控的研究成品端評估,而非田野資料。)語料是合成的,由作者的腳本產生。1
| 防禦 | AC-1 | AC-1.a | AC-1.b | 成本與限制 |
|---|---|---|---|---|
| 故障關閉政策閘門 | 100% 攔下 | 100% 攔下 | 未測量 | 1.0% 誤報;在簡單的自適應基準中,100% 的樣本繞過了它 |
| 回應端異常篩查 | 89.0% 被標記 | 50.0% 被標記 | 50.9% 被標記 | 6.7% 誤報 |
| 僅允許追加的透明日誌 | 僅供稽核 | 僅供稽核 | 僅供稽核 | 每筆約 1.26 KB |
政策閘門會攔下「fetch from non-allowlisted domains or install packages outside a local allowlist」(從允許清單以外的網域下載,或安裝本機允許清單以外之套件)的 shell 命令。在 1,000 個良性命令、500 個 AC-1 樣本與 500 個 AC-1.a 樣本上,它攔下了所有攻擊樣本,而所有誤報都是從允許清單漏列的鏡像網域下載安裝程式。作者稱它為「the strongest immediately deployable control」(目前可立即部署的最強控制措施)。他們也坦白報告了它的失效情形。如果攻擊者把酬載放在允許清單內的網域上,或預先放置一支本機腳本、再由看似良性的命令執行,「the policy gate is bypassed in 100% of samples」(政策閘門在 100% 的樣本中被繞過)。他們的結論是:這道閘門「should be understood as a coarse but useful filter for obvious high-risk workflows, not as a robust defense against defense-aware attackers」(應理解為針對明顯高風險工作流程的粗略但有用的過濾器,而非能抵禦了解防禦機制之攻擊者的穩固防線)。1
對打造這道閘門的人而言,有一個細節很重要,而論文在這點上前後不一致。第 4 節把閘門描述為只檢查網域,並設計 AC-1.a 來擊敗它:「the gate blocks non-allowlisted domains but does not maintain a per-package allowlist」(閘門會攔下非允許清單內的網域,但不維護逐套件的允許清單)。第 7 節的閘門,也就是對 AC-1.a 拿到 100% 的那一個,同樣會攔下「outside a local allowlist」(本機允許清單以外)的安裝。依我的解讀,少了套件清單的閘門會完全漏掉拼寫搶註(typosquat)式的替換。
異常篩查依據本機可見的特徵(例如 shell 風險樣式與參數熵)為每個回傳的工具呼叫評分,沒有上游原始版本可供比對。在 6.7% 的誤報率下,它標記出 89.0% 的 AC-1 樣本、50.0% 的 AC-1.a 與 50.9% 的 AC-1.b。在 1% 的誤報預算下,它抓到 43.5% 的 AC-1,而 AC-1.a 幾乎一個也沒抓到。作者評其「useful for review prioritization」(有助於排定審查優先順序),效果弱於閘門。1
透明日誌記錄請求、回應、路由器 URL、TLS 中繼資料,以及原始回應的雜湊值。它什麼也防止不了。它讓您在事件發生後,能回答某個路由器或某組憑證影響到多遠,以及哪些工作階段經過了它。1
作者要求的修復
三種控制措施都無法驗證工具呼叫的來源。作者直言:「No client-side control available today can prove that a router preserved the upstream provider’s response.」(目前沒有任何客戶端控制措施能證明路由器保留了上游供應商的回應。)1
能做到這點的是供應商的簽章。論文提出「a provider-signed canonical response envelope, similar in spirit to DKIM for email」(由供應商簽章的標準化回應信封,精神上類似電子郵件的 DKIM),涵蓋模型識別碼、工具名稱、工具參數、結束原因與客戶端 nonce,由客戶端在執行任何工具呼叫前驗證。論文指出目前沒有人提供這種機制:「To our knowledge, none of the major provider tool-use APIs or the current MCP specification expose a deployed response-signing mechanism for tool-call arguments today.」(據我們所知,目前主要供應商的工具使用 API 與現行 MCP 規格,都沒有為工具呼叫參數提供已部署的回應簽章機制。)1
這項提案有兩個限制。傳輸安全無法取代它:雙向 TLS 與憑證綁定「can authenticate the router endpoint the client chose, but they do not say whether the returned tool call preserves upstream semantics」(能驗證客戶端所選的路由器端點,卻無法說明回傳的工具呼叫是否保留了上游的語意)。簽章對被竊的機密也毫無幫助:「AC-2 cannot be mitigated by response-signing proposals because the secrets are exposed on the request path before any provider-side mechanism can act.」(回應簽章提案無法緩解 AC-2,因為機密在任何供應商端機制能介入之前,就已在請求路徑上曝露。)1
您實際上該做什麼
如果您的代理是透過一個不是您自建的路由器呼叫模型:
- 能直連就直連,不能直連就要了解營運者。加入路由器只需要改一個 base URL,正因如此,它往往在沒有任何決策的情況下就被加了進去。請把它當成一項決策。這裡的「信任」意指外部依據,例如一支您認識的團隊、一份合約,或一個您能據以主張權利的法域。市集評價不算。
- 對高風險工具呼叫採取故障關閉。在 Claude Code 中,這就是一個 PreToolUse 掛鉤,攔下從允許清單以外網域下載的 shell 命令,以及允許清單以外的套件安裝。務必保留套件清單:相依套件替換變體正是為了擊敗只檢查網域的閘門而存在。請把掛鉤寫成凡是無法解析的一律拒絕,並以結束碼 2 或
deny決策回應:Claude Code 會把其他結束碼與逾時視為不阻擋,因此當掉或卡住的掛鉤擋不下該呼叫,呼叫會照常進入一般的權限流程,而在自動核准的工作階段中就直接執行。也請在第一次呼叫時確認掛鉤確實執行了。文件警告:「a mistyped path in settings.json leaves the gate silently disabled」(settings.json 中打錯的路徑會讓閘門無聲無息地失效)。請預期兩份清單都需要維護,也請預期有決心的攻擊者會繞過它們。3 - 絕不讓自動核准的工作階段經過您無法掌控的路由器。誘餌研究中的 401 個工作階段就是前例。在被改寫的工具呼叫與其執行之間,權限提示是少數能擋住的東西之一。
- 讓機密遠離流量。被動蒐集不會改變任何您觀察得到的東西。提示、工具結果,或代理讀取的檔案中的任何內容,都會以明文經過路由器。請嚴格限縮憑證範圍,盡可能讓它們不進入上下文,並輪換任何曾經過您事後起疑之路由器的憑證。
- 在本機記錄日誌。記錄請求、回應、路由器 URL 與回應雜湊值,先遮蔽請求中的機密,再存放在路由器碰不到的地方。它擋不住攻擊,但能在事後告訴您曝露了什麼。
- 在沙箱中執行。論文指出,沙箱「reduce post-execution blast radius but do not authenticate where a tool call came from」(能縮小執行後的爆炸半徑,卻無法驗證工具呼叫的來源)。取前半句就好。
令人不安的推論
路由器層是代理生態系「基礎設施出貨速度快過安全保障速度」的鮮明範例。人們想要一把金鑰通用所有模型、想要更低的價格,也想從供應商未服務的地區取得存取。路由器三者兼備,市場也給予回報。
同樣的劇本已在 MCP 層、套件層與擷取內容層上演過。代理堆疊出現新的一層。開發者在任何人審查之前就採用了它。攻擊者先到,研究人員隨後。這一次,研究人員統計了 428 個路由器:9 個注入惡意程式碼,17 個使用植入的憑證,1 個掏空錢包,還有 401 個自動核准的工作階段流經包含研究人員誘餌在內的中繼路徑。1
能補上缺口的那一塊,也就是由供應商簽章的回應,並不是營運者能自行加上的。在供應商提供之前,上述控制措施能降低曝露程度,但沒有一項能證明某個工具呼叫確實出自模型本身。
2026年10月1日更新:您自己託管的路由器也在名單上
本文談的是別人運行的路由器。安全公告紀錄補上了另一半。2026年10月1日,PyPA 安全公告資料庫新增了 11 則針對 LiteLLM 的條目;LiteLLM 是一個開源代理伺服器,團隊基於上述理由(一把金鑰通用所有模型)自行託管它。2這些條目都不是新的。其中 9 則自 6月21日起就已列在 GitHub 安全公告資料庫與 NVD 中,第 10 則自九月中旬起;對代理伺服器而言最重要、因為會曝露供應商金鑰的那一則,於 8月26日在 LiteLLM 自己的儲存庫發布,修正版則自 8月9日起已在 PyPI 上;GitHub 將其評為 Moderate。之所以值得放在一起讀,是因為它們揭示了這一層的狀況,也因為 8月9日之前發布的每一個正式版本,都落在 CVE-2026-84377 的影響範圍內。
最該先讀的是 CVE-2026-84377。儲存庫安全公告的原文是:「Any authenticated LiteLLM proxy user could redirect an outbound provider call to a destination they control and cause the proxy to send its own configured provider credentials to that destination.」(任何經過驗證的 LiteLLM 代理伺服器使用者,都能把對外的供應商呼叫重新導向到自己控制的目的地,並讓代理伺服器把自身設定的供應商憑證送往該處。)成因在於檢查的形式:「The proxy’s request-body validation was a denylist that did not cover every sensitive parameter and did not inspect parameters nested inside other request fields.」(代理伺服器對請求主體的驗證是一份拒絕清單,既沒有涵蓋所有敏感參數,也不檢查巢狀在其他請求欄位中的參數。)它在九條發行線中修正,從 1.88.6 到 1.96.2。對暫時無法升級的人,安全公告用一句話列出三種因應措施:「Set general_settings.allow_client_side_credentials to false so callers cannot override connection parameters, restrict proxy keys to trusted callers, and block the affected parameters (api_base, base_url, model_list, fallbacks, provider credential fields) at a reverse proxy or API gateway.」(把該設定設為 false,讓呼叫者無法覆寫連線參數;把代理伺服器金鑰限於受信任的呼叫者;並在反向代理或 API 閘道攔下受影響的參數。)2單靠第一項並不夠。在受影響版本 1.95.0 的原始碼中,只有當該設定為 true 時,請求主體檢查才會被完全跳過(部署的 configurable_clientside_auth_params 仍可豁免個別參數),因此從未動過此設定的安裝本來就是 false,而那份不完整的檢查正是安全公告所描述的缺陷。升級才是修正之道。6
第二則條目 CVE-2026-59823 是同一缺陷的縮小版:防護機制「blocks the api_base and base_url parameters but does not cover user_config」(攔下 api_base 與 base_url 參數,卻沒有涵蓋 user_config),因此持有有效虛擬金鑰的呼叫者可以在其中放入 api_base,把代理伺服器指向任何主機。該問題已在 1.83.9 修補,此版本自 4月17日起即在 PyPI 上;其安全公告則到九月才發布。4
有兩則條目涉及代理伺服器的 MCP 部分:MCP 代理中的不當驗證(CVE-2026-12773,版本範圍止於 1.84.0 的修正),以及透過 MCP OpenAPI 規格載入器的 spec_path 參數進行的伺服器端請求偽造(CVE-2026-12798,記錄為影響至 1.82.2 為止的版本)。其餘七則涵蓋管理員金鑰處理、SSO 除錯流程、SSO 工作階段失效、所產生金鑰的工作階段到期、UI 中的使用者列舉、非同步端點上的防護欄繞過,以及機器對機器 JWT 處理。5
這九筆紀錄比前兩筆單薄。它們的描述沿用 VulDB 的制式措辭,而非維護者撰寫的說明;MCP 代理那一則的描述寫著「up to 1.59.8」(至 1.59.8 為止),版本範圍卻寫著在 1.84.0 修正。5CVE-2026-84377 的紀錄本身也有不一致:儲存庫頁面列出受影響版本為「<1.94.0」,而經審查的紀錄所列範圍卻涵蓋 1.96 發行線,直到 1.96.2 修正為止。2
請拿這些範圍對照您實際運行的版本,不要相信任何摘要,包括這一篇。所有記錄的範圍都止於 1.96.2 或更低,因此自 1.97.0 起的版本(8月16日起已在 PyPI 上)不在這 11 則的任何範圍內;10月1日時的最新版本是 1.103.2。5
路由器的危險在於憑證所在之處,無論營運者是誰。一個觸碰植入 AWS 金鑰的市集路由器,與一個能被說服送出供應商金鑰的自行託管代理伺服器,是從兩個方向抵達的同一種曝露:一個經由營運者,一個經由任何持有虛擬金鑰的租戶。上文各節的防禦措施,都假設您是客戶端。
對於您自己運行的代理伺服器,請補上營運者的那一半:把每一把虛擬金鑰都當成通往其背後上游金鑰的憑證;除非確實需要,否則關閉由客戶端提供的連線參數,包括各部署的 configurable_clientside_auth_params;並讓代理伺服器與其他持有機密的系統遵循同樣的修補時程。
如果在代理伺服器運行受影響版本期間,有任何您不完全信任的人持有虛擬金鑰,請輪換供應商金鑰與代理伺服器上設定的其他所有機密,並檢查其日誌中是否有對您未設定之主機的對外呼叫;安全公告所述的影響涵蓋「other configured secrets」(其他已設定的機密)以及對「internal services reachable from the proxy」(代理伺服器可觸及之內部服務)的請求。11 則條目中有兩則,也就是 MCP 代理中的 CVE-2026-12773 與 SSO 除錯流程中的 CVE-2026-12795,是 1.84.0 以下版本的驗證缺陷,因此在這些版本上,誰持有金鑰未必能界定誰能觸及代理伺服器;如果它可從您不信任的網路連線,請同樣進行輪換。
四月版錯在哪裡
4月10日版的本文是根據論文摘要撰寫的。2026年10月2日對照全文重讀後,以下各處有誤或具誤導性,均已在上文更正:
- AC-1.a。我把它描述為一種「only fires when the request matches a specific dependency or context」(只在請求符合特定相依套件或脈絡時才觸發)的注入。實際上它是安裝命令中的套件名稱替換。觸發條件屬於 AC-1.b。
- 防禦措施。我寫道「the abstract does not rank the defenses」(摘要並未對防禦措施排序),並以個人意見提出排序。論文正文測量了全部三種,稱政策閘門為「the strongest immediately deployable control」(目前可立即部署的最強控制措施),並報告它在簡單的自適應基準中於 100% 的樣本被繞過。我漏掉了這一點。
- 簽章建議。我要營運者在客戶端簽署請求、在上游驗證,並稱之為「the only real fix」(唯一真正的修復)。論文要求的是相反方向:由供應商簽章回應、由客戶端驗證,並指出簽章無法解決機密遭竊的問題。
- 洩露的金鑰。我說那把金鑰是「as if it had been exposed through a developer mistake」(彷彿因開發者疏失而曝露)而洩露的,並下結論說「The router was a laundering layer for a stolen key.」(路由器是被竊金鑰的洗錢層。)實際上它是洩露在路由器營運者分享憑證的論壇與聊天群組中,而論文表示無法總是分辨重用它的是路由器營運者、無關的第三方,還是較長的中繼鏈。
- 憑證數字。重點回答區塊寫著「17 of 28 paid routers touched planted AWS credentials」(28 個付費路由器中有 17 個觸碰了植入的 AWS 憑證),描述也說研究人員測試了 28 個路由器。論文的付費列並未觀察到任何憑證濫用。使用 AWS 誘餌憑證的 17 個路由器,以及掏空 ETH 的那一個,全都屬於 400 個免費路由器。
- 掛鉤建議。我建議使用能「validate response shapes」(驗證回應結構)的 PostToolUse 掛鉤。PostToolUse 掛鉤在工具執行之後才運行,對被改寫的命令而言為時已晚。論文測試的控制措施是執行前的閘門,在 Claude Code 中就是帶允許清單的 PreToolUse 掛鉤。
- 較小的錯誤。論文有六位作者,不是五位。論文並未說路由器「knows when it is being sampled」(知道自己何時被取樣);那是我的渲染。開頭把主題稱為「MCP trust chains」(MCP 信任鏈),但論文研究的是路由器,不是 MCP。我把沉默的對外流量那篇文章描述為關於工具描述,實際上它談的是藏在擷取內容中的指令。
FAQ
在此脈絡下,LLM API 路由器是什麼?
一種服務,接收統一格式(通常與 OpenAI 相容)的請求,選擇一個上游模型供應商,再回傳回應。它是一個能以明文存取每一個請求與回應的應用層代理伺服器。1
TLS 能保護我免受惡意路由器侵害嗎?
不能。客戶端把路由器設為自己的端點,因此路由器會終止客戶端的 TLS 工作階段,再另外向上游開啟一個。TLS 保護每一段線路,卻無法保護酬載不受路由器侵害。1
我要如何偵測正在改寫工具呼叫的路由器?
靠測試並不可靠。研究中有兩個路由器只在 50 次請求之後、或只針對 Rust 或 Go 專案的自動核准工作階段才注入,論文的結論是「no fixed-length client test can guarantee that the router is benign」(沒有任何固定長度的客戶端測試能保證路由器是良性的)。針對 shell 命令與套件安裝的故障關閉允許清單能擋下單純的案例,而論文也顯示,了解防禦機制的攻擊者能繞過它。1
PreToolUse 掛鉤有幫助嗎?
有,作為政策閘門。掛鉤看到的是客戶端收到的工具呼叫,如果路由器改寫過,看到的就是改寫後的版本;它能攔下從未列入清單的網域下載、或安裝未列入清單之套件的命令。它無法判斷該呼叫是否就是模型所產生的內容。13
我直接對 api.anthropic.com 執行 Claude Code。我會受影響嗎?
不會受到本論文所述路由器攻擊的影響,因為中間沒有任何中介。如果您基於任何原因(例如企業閘道或模型聚合器)讓 Claude Code 經過代理伺服器,那個代理伺服器就處於同樣的位置。
那 OpenRouter、LiteLLM 或其他知名聚合器呢?
論文測量了來自三個市集的 28 個付費路由器,以及大多以兩個開源範本建置的 400 個免費路由器。它在背景介紹中提到 LiteLLM 與 OpenRouter,但並未測試它們。這個結構性論點適用於任何路由器:它能讀取並改寫流量,而知名度與完整性是兩種不同的屬性。至於您自行託管的代理伺服器,上方 10月1日的更新涵蓋了 11 則 LiteLLM 安全公告。
那 401 個自動核准的工作階段是誰的?
是流量抵達研究人員誘餌中繼的第三方。如果您讓自動核准的代理工作階段經過一個不是您自建的路由器,請停止這麼做,輪換每一組曾經過它的憑證,並檢查工作階段日誌中是否有您預期之外的工具呼叫。
參考文獻
-
Hanzhi Liu、Chaofan Shou、Hongbo Wen、Yanju Chen、Ryan Jingyang Fang 與 Yu Feng,“Your Agent Is Mine: Measuring Malicious Intermediary Attacks on the LLM Supply Chain,” arXiv:2604.08407v1,2026年4月9日,列於 ACM Conference on Computer and Communications Security(2026年10月)。全文閱讀於 2026年10月1日與2日。使用的章節:引言與 2.1(路由器是什麼、四跳範例、TLS 終止);2.2(沒有供應商層級的完整性機制);4.1 與 4.2(四種攻擊類別、
requests變成reqeusts的範例、五類觸發條件);5.1(四階段管線與各項定義);5.2 以及表 3、表 4(1 個付費與 8 個免費路由器注入、2 個免費路由器帶觸發條件、17 個免費路由器使用 AWS 誘餌憑證、1 個掏空 ETH);5.3(洩露的金鑰與誘餌:1 億個 token、七個以上的 Codex 工作階段、來自 147 個 IP 的 4 萬次以上存取嘗試、約 20 億個 token、約 13 GB、99 組憑證、440 個工作階段、398 個專案或主機、401 個處於 YOLO mode);5.4(主要發現,包括關於付費存取的那句話);5.5(範圍);6 與表 5(Mine、四個框架、每個模組 1,000 次請求、100% 與 99.6% 的相容性);7 與表 6(三種防禦及其結果);8.2 與 8.3(簽章回應信封、MCP);9(MCP 伺服器與路由器所處位置的差異);附錄 A(資料留存、憑證退役、不到 50 美元的掏空金額、Mine 未公開)。 ↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩ -
LiteLLM 儲存庫安全公告 GHSA-3cv6-jpf6-8222(CVE-2026-84377、PYSEC-2026-4066),”Authenticated SSRF and provider-credential exfiltration via unvalidated request-body routing parameters”(透過未經驗證的請求主體路由參數進行的經驗證 SSRF 與供應商憑證外洩),2026年8月26日於儲存庫發布,NVD 於 9月2日收錄,GitHub 於 9月30日審查;2026年10月1日於儲存庫頁面及透過 OSV 紀錄閱讀。Impact 與完整的 Workarounds 句子引自該安全公告;OSV 紀錄的 Patches 一行寫著「Fixed in 1.96.2, 1.95.1, 1.94.3, 1.93.2, 1.92.2, 1.91.5, 1.90.7, 1.89.7, and 1.88.6」,儲存庫頁面則列出「Affected versions <1.94.0」。「11 則」這個數字,指的是 PyPA 安全公告資料庫中 PYSEC-2026-4066 至 PYSEC-2026-4076 的條目,全部針對
litellm套件,日期皆為 2026年10月1日,無一撤回。 ↩↩↩↩↩ -
Anthropic,Hooks reference,Claude Code 文件,2026年10月2日擷取。PreToolUse 的執行時機是「Before a tool call executes. Can block it」(工具呼叫執行之前,可加以阻擋);PostToolUse 的執行時機是「After a tool call succeeds」(工具呼叫成功之後);「Any other exit code doesn’t block on its own for most hook events」(對大多數掛鉤事件而言,其他結束碼本身不會造成阻擋);逾時的命令掛鉤「doesn’t block the tool call」(不會阻擋該工具呼叫);而且「a mistyped path in settings.json leaves the gate silently disabled.」(settings.json 中打錯的路徑會讓這道關卡在無聲無息中失效。) ↩↩↩
-
GitHub 安全公告 GHSA-hx8v-g79f-8w5f(CVE-2026-59823、PYSEC-2026-4070),”LiteLLM Proxy has server-side request forgery via the
user_configrequest parameter”(LiteLLM Proxy 可經由user_config請求參數進行伺服器端請求偽造),2026年9月17日發布,2026年10月1日透過 OSV 閱讀:受影響版本<= 1.83.8,修補版本1.83.9,安全公告將該版本的發布日期記為 2026年4月17日。 ↩ -
2026年10月1日閱讀的 OSV 紀錄:PYSEC-2026-4067(CVE-2026-12773,MCP 代理驗證)、PYSEC-2026-4069(CVE-2026-12798,MCP OpenAPI 規格載入器),以及 PYSEC-2026-4068、4071、4072、4073、4074、4075 與 4076。這九則背後的 GitHub 安全公告於 2026年6月21日發布,也就是 NVD 收錄它們的同一天,而且每一則都引用 VulDB。上傳日期與最新版本取自 PyPI 專案頁面及其 JSON,於同日讀取:1.88.6 至 1.95.1 於 8月9日,1.96.2 於 8月11日,1.97.0 於 8月16日,1.103.2 於 10月1日。 ↩↩↩
-
作者對 PyPI 上
litellm1.95.0 wheel 的解讀(受影響範圍為 1.95.0,至 1.95.1 修正),2026年10月1日:在litellm/proxy/auth/auth_utils.py中,當general_settings.get("allow_client_side_credentials") is True時,_check_banned_params會在拒絕任何東西之前直接返回;否則,請求主體若帶有禁用清單上的參數就會被拒絕,除非部署的configurable_clientside_auth_params允許該參數。在該版本中,禁用清單涵蓋vertex_ai_credentials以及可觀測性服務的憑證與主機。我讀的是一個受影響版本,而非全部九條發行線。 ↩