返回所有文章
發佈於 · 作者 Renaud Deraison

那道閘道接受任何一個權杖

9 月 2 日,CISA 把七個已遭利用的漏洞加進 KEV 目錄。其中三個是 AI 基礎設施,這是 AI 佔了將近一半的第一批。讀讀 LiteLLM 那一條:它的 MCP 端點會放行一個帶著任何 Bearer 權杖的未認證請求,因為金鑰驗證失敗後掉進了一個空的認證物件,而呼叫方接著就能列出並呼叫閘道所接上的每一個工具。Bromure Agentic Coding 把同樣的權限放在一個 hypervisor 與一條 vsock 之後:沒有在聽的連接埠、沒有可偽造的標頭、也沒有退路分支。

你建起這道閘道,是為了不讓任何一台筆電握著全部的金鑰。它成功了,於是它握著 全部的金鑰。後來它的前門接受了一個由某人隨口編出來的 Bearer 權杖。

9 月 2 日,CISA 新增了七個漏洞 到 Known Exploited Vulnerabilities 目錄。兩個 SonicWall SMA1000 的問題、一個 Sangoma Switchvox 的 SQL 注入、一個 JFrog Artifactory 的認證繞過、一個 Kestra 的作業系統命令注入、一個 Starlette 的 HTTP 走私缺陷,以及 BerriAI 的 LiteLLM 的一個不當認證缺陷。

這七個裡有三個是 AI 與 ML 基礎設施。Forkast 稱之為 AI 元件佔了新增項目將近一半的第一批 KEV, 那種先掙得一則標題、然後就被歸檔的里程碑。但看看它們是哪一種 AI 基礎設施, 這一批就值得多過歸檔。三個都坐在中間:它們正是你部署下去,好讓你的代理人與 它們所碰觸的資源不再直接對話的元件。就從 LiteLLM 那一條開始。

那條退路分支

LiteLLM 是一道 AI 閘道:一個代理,站在你的代理人與模型供應商之間,替所有人 保管供應商金鑰,執行預算與速率限制,寫下日誌,如今還往 Model Context Protocol 伺服器展開,好讓一個團隊的代理人共用一個 MCP 端點,而不是每位開發者各自接上 自己的。

CVE-2026-59822 於 7 月 8 日公開, 在 GitHub 的公告中帶著 8.8 的 CVSS, 就住在那個 MCP 端點上。公告裡的一句話承載了整個故事:

退路路徑可能以一個空的 UserAPIKeyAuth() 物件,取代失敗的 LiteLLM 金鑰 驗證。

MCP Streamable HTTP 端點支援 OAuth2 直通,好讓一個原本要給上游 MCP 伺服器的 權杖能穿過閘道,而不是拿去和 LiteLLM 自己的金鑰庫核對。這是個合理的功能, 而它需要一個分支:先試 LiteLLM 的金鑰,如果送來的不是金鑰,就走直通那條路。

你靠「失敗」抵達那條分支。送出一個 Authorization 標頭,裡面放任何字串都行, 金鑰驗證因為那字串不是金鑰而拒絕它,於是處理程式建出一個空的 UserAPIKeyAuth(),帶著它繼續往下走。你的請求此刻握著一個不屬於任何人的、 已認證的 MCP 工作階段。從那裡,你可以把閘道設定過的每一個 MCP 工具都列出來 並呼叫:內部應用程式、資料庫、雲端主控台、開發系統,團隊接上去的任何東西。 這個缺陷影響 1.84.0 之前的所有版本。CISA 給聯邦機關的修補期限是 9 月 16 日, 而 LiteLLM 給今天無法升級者的建議是:在前面的反向代理上擋掉 /mcp/

CVE-2026-59822 — 失敗所抵達的那條分支未認證的請求POST /mcp/Authorization: Bearer anything-at-all驗證 LiteLLM 金鑰那字串不是金鑰驗證失敗OAuth2 直通退路代入一個空的認證物件後繼續UserAPIKeyAuth()一個不屬於任何人的已認證 MCP 工作階段列出每一個設定過的工具 · 想呼叫哪個都行 · 從未出示過 LiteLLM 金鑰內部應用程式在閘道後面資料庫當初接上去的任何東西雲端服務帶著閘道的觸及範圍開發系統原始碼、CI、工單
MCP 處理程式先試 LiteLLM 的金鑰,失敗時就走 OAuth2 直通那條路,那條路建出一個空的 UserAPIKeyAuth() 並繼續。一個帶著編造 Bearer 權杖的請求,抵達了有效金鑰本該抵達的地方:一個已認證的 MCP 工作階段,背後掛著閘道完整的工具清單。

另外三個,壞在同一個函式

把這一批剩下的讀完,你會得到比「AI 對非 AI」那則標題更有用的東西。

Starlette,CVE-2026-48710,綽號 BadHost。Starlette 是 FastAPI 底下的 ASGI 工具組,Python 的 AI 服務有很大一部分建在它上面,LiteLLM 也在其中。一個特製的 Host 標頭把一段路徑注入到請求的主機部分,這改變了 URL 被重建的方式,也就 意味著以路徑為準的認證中介軟體評估的是一條路由,而應用程式端出的是另一條。 CISA 的條目寫明了結果:當認證取決於重建後 URL 的路徑時,會發生認證繞過。它的 CVSS 是 6.5,是這一批裡最低的分數,也是最說不出後果的那個分數。LiteLLM 在 1.84.0 也交出了 針對同一形狀的自家配套修正, 描述為「一個特製的 Host 標頭可能讓代理的認證關卡去評估一條與實際端出者不同 的路由」,並告訴那些代理曾對外曝露的維運者:輪換金鑰,稽核管理日誌。

JFrog Artifactory,CVE-2026-82329,CVSS 9.8。預設組態下的不當認證:一把 「幽靈」join key,讓未認證的攻擊者能偽造管理員權杖。watchTowr 在 9 月 1 日, 也就是揭露後四天,記錄到真實環境中的利用:攻擊者鑄造管理員憑證並列舉使用者。 這是製品倉庫,是替整間組織回應 npm installpip install 的那台主機。

Kestra,CVE-2026-49869。未認證的遠端攻擊者可觸及的作業系統命令注入, 他不需要任何憑證,就能建立並執行任意工作流程。編排器做著編排器該做的事—— 只是替一個從未登入過的人做。

四個產品,四套不同的程式碼庫,而每一套裡壞掉的是同一個函式:決定這個請求 夠不夠格待在這裡的那一個。一個空的認證物件。一條重建出來的路徑。一把預設的 join key。至於 Kestra,是那條負責執行的路由上根本沒有檢查。

攻擊者接下來做的事很無聊,而無聊正是規模化作業的跡象。The Hacker News 整理了觀察到的行為: 反向 shell、XMRig 挖礦程式、憑證列舉,以及竊取 API 金鑰與 LLM 供應商憑證。 Microsoft 總結道:

觀察到的目標是一致的。在這些案例中,遙測資料顯示了憑證蒐集、持久存取機制與 資源變現,儘管執行路徑因產品而異。

一批漏洞,一個函式,四種做錯的方式元件做出決定的那個檢查它放了什麼過去LiteLLM · AI 閘道CVE-2026-59822 · 8.8驗證 LiteLLM 的 API 金鑰在 MCP 端點上任何一個 Bearer 權杖失敗掉進了一個空的認證物件Starlette · ASGI 工具組CVE-2026-48710 · 6.5以路徑為準的認證中介軟體作用在重建後的 URL 上一條它從未守過的路由特製的 Host 標頭改變了重建方式JFrog Artifactory · 登錄庫CVE-2026-82329 · 9.8join key 驗證在預設組態之下偽造的管理員權杖揭露四天後就在真實環境遭利用Kestra · 編排器CVE-2026-49869沒有,在那條負責執行的路由上建立與執行工作流程任意命令,未經認證完全不需要憑證
同一批 KEV 裡的四個條目,四套程式碼庫,一個函式。每個產品都壞在它判斷一個請求是否屬於此處的那一點,而四個裡有三個,正是你為了集中存取、而不是讓每位開發者各自握著自己的一份,才部署下去的元件。

閘道是對的主意,而這是它的帳單

AI 閘道是這兩年比較好的主意之一,而多數團隊走到這一步都有紮實的理由。

在閘道之前,每位開發者的編碼代理人都把一把供應商金鑰放在環境變數裡,每位 開發者用自己的權杖接上自己的 MCP 伺服器,而沒有人答得出這一切花了多少、 碰過什麼。在中間放一道閘道,一次就把這些都解決了。一個地方保管供應商金鑰與 MCP 設定,並寫下日誌。輪換一把金鑰變成單一動作,切斷一位離職員工也是。 這個產業會收斂到這裡,是有道理的。

帳單是:你蓋了一個網路服務,它存在的全部意義就是替所有人保管權限,而它用 一個函式來決定你是誰。函式有分支,其中一條分支負責處理「你先試的那件事沒成」 這種情況。

這個形狀不是 LiteLLM 發明的。每一道閘道都有前門,每一扇前門都有一個認證函式, 而一個支援兩種憑證方案的認證函式,會有一條在第一種失敗時走的路。門後那片 觸及範圍是刻意的:把它集中起來,正是這項產品本身。所以當一道閘道出事時, 它保管的一切早就都在同一個房間裡了。

從這種事故通常被導出的結論是「回去用每位開發者各自的金鑰」,而那在任何一項 量測上都更糟。繼續集中。真正值得爭論的問題是那個集中起來的東西坐在哪裡, 以及它是否非得回應陌生人的請求才能完成它的工作。

Bromure 把同一份工作放在哪裡

Bromure Agentic Coding 讓每一個編碼代理人跑在你自己 Mac 上、由硬體虛擬化的 Linux VM 裡,而每一項安全控制都坐在那道邊界的主機側。這些控制和一道閘道提供的 是同一份清單:保管憑證、居中處理 MCP 伺服器、分類代理人對你基礎設施做的事、 留下紀錄。網路上沒有任何東西能碰到執行這些控制的元件。

沒有前門。 客體的 HTTPS 從不經由網路離開。它走一條 virtio 通訊端、vsock 連接埠 8443,隧道到主機端的代理,而手冊對拓樸講得很直白:這台 VM 沒有其他通往 網路的路徑。那個代理沒有任何在聽的連接埠,能讓你的區網、你的公司網路或網際 網路定址得到。它從不問是誰在呼叫,因為那條通訊端的另一端只有一樣東西,而 那是什麼由 hypervisor 決定。沒有 Authorization 標頭、沒有金鑰庫、沒有方案 協商,因此也沒有第一種方案失敗時要走的分支。

MCP 的 bearer 權杖,在抵達代理人之前就已經是假的。 對於工作區裡設定的 HTTP 傳輸 MCP 伺服器,真正的 bearer 權杖始終加密留在 Mac 上。代理人的設定 拿到的是一個 brm-mcp_… 佔位值。設定面板把那個欄位標示為絕不送進 VM—— 由代理替換,而主機端的代理會在線路上換進真值,範圍限定為該伺服器主機的完全 相符或子網域相符,別無其他。代理還會對該伺服器的 OAuth 與 OIDC 探索路徑回 404,好讓 Claude Code 把那台伺服器當成已預先認證,而不是去嘗試一段 VM 永遠 完成不了的瀏覽器流程。LiteLLM 的退路,正是站在同一片直通表面上。在這裡, 工作區根本沒有可以失手的憑證。

每一次操作,都有自己的一次判斷。 一道閘道在門口把問題了結:過了門, 後面的一切都是你的。Bromure 的 Guardrails 在代理內部分類代理人的呼叫,橫跨 Kubernetes、AWS、git 託管站、容器登錄庫與資料庫,並套用逐服務的寫入政策。 新工作區預設是寫入前先問,於是讀取照流,而每一次變更都會停在主機側的一個 對話框上,上面顯示字面上的那個操作:確切的 SQL,或 方法 /路徑唯讀 擋掉一切變更。使用前先問則管住憑證本身,授權以分鐘計。就算是一個偽造的 工作階段,也依然沒辦法靠 kubectl delete 一路推過去,因為第二次判斷發生在 你的 Mac 上,和第一次的結果毫無關係。

預設拒絕,在兩個方向上都回答了可達性。 工作區的出向防火牆是一張有序的 規則表,並為未相符的流量設定預設值。把它設成拒絕,這台 VM 就只能到達你 列出的主機,在任何協定上都是如此。客體之外的兩個元件執行這件事:虛擬交換器 以目的 IP 與從 DNS 側錄到的主機名比對每一條流,而代理再以 TLS 伺服器名比對 一次。編輯不必重開就會送到進行中的工作階段。反方向上,NAT 模式讓這些 VM 待在 你的實體區網之外,除非你刻意發布某個服務,否則外面沒有任何東西能對它們開啟 連線。那些把曝露閘道一網打盡的掃描器,從一台 Mac 得不到任何回應。

一道被拿下的閘道送回來的任何東西,都是不可信輸入。 這一點比那個 CVE 活得 更久。一旦閘道歸了別人,模型輸出、工具結果與錯誤字串就全都來自攻擊者,而且 走在整個堆疊裡權限最高的那條線上:代理人天生就會據以行動的那條。Bromure 的 原始碼偵測器會用一個本地的 PromptGuard 模型,在裝置上,於模型據以行動之前, 為代理人外送 AI 流量中的 tool_result 區段評分,並可以記錄、詢問或阻擋; 一次阻擋會回傳 HTTP 451,模型永遠看不到那段內容。

外洩那一段會朝關閉的方向失敗。 工作區裡的每一份憑證,都是一個只有單一 合法目的地的確定性假貨。代理會掃描每一個外送請求,找出正要前往鑄造範圍以外 之處的假貨;找到時,它一個位元組也不轉發就拒絕請求,暫停這台 VM 並發出警報, 以一列紅色的憑證仲介記錄在 Security Timeline 上。因為當初能被碰到的只有 假貨,真憑證不需要輪換。

權限在中間一個服務,任何路由得到它的東西都能碰到代理人 · 筆電 A代理人 · 筆電 B一個陌生人編出來的權杖AI 閘道在 :443 聆聽認證函式讀一個標頭保管每一把 API 金鑰保管每一條 MCP 接線一條分支決定一切模型供應商 · 資料庫 · 雲端 · 內部應用用的是閘道的權限,不是呼叫方的攻擊者需要什麼一條通往連接埠的路徑,加上檢查裡一條壞分支權限在邊緣一台 Mac、一個 hypervisor、沒有東西在聽工作區 VM · 代理人在這裡跑brm-mcp_… · sk-ant-api03-brm-… · ghp_…只有佔位值——客體裡沒有真的機密vsock 8443 — 唯一的出口主機端代理 · 在你的 Mac 上沒有在聽的埠 · 沒有可偽造的標頭 · 沒有退路換進真憑證,範圍限定單一主機出向防火牆 · 寫入政策 · 注入掃描每個請求一列軌跡,就在你的機器上攻擊者需要什麼一條不存在的路徑,通往一個沒開的連接埠
左邊:權限在中間。一個網路服務保管所有金鑰與所有 MCP 接線,而只有一個認證函式站在它與任何能路由到它的人之間。右邊:權限在邊緣。同樣的控制跑在你的 Mac 上、hypervisor 的後面,經由一條沒有在聽的連接埠的 virtio 通訊端抵達。VM 只持有假貨,而代理是在請求離開客體之後,才逐一裁決每個操作。

如果你今天在營運一道閘道

把 LiteLLM 升級到 1.84.0 或更新;若做不到,就在前面的反向代理上擋掉 /mcp/。接著照 LiteLLM 自己針對 Host 標頭修正給出的建議,把一個曾經曝露的 代理當成一個曝露的金鑰庫來處理:輪換它保管過的供應商金鑰,稽核管理日誌。 聯邦機關在 CVE-2026-59822 上的期限是 9 月 16 日,那是個可以借來用的合理 日期。

那條讓問題變小的規則

在工作區的 Guardrails 面板裡,把未相符流量設成拒絕,然後列出 這份工作真正需要的東西:allow web api.github.comallow web registry.npmjs.orgdefault deny。一個不被允許碰到內部閘道的 工作區,也就不能被拿來穿過任何一道閘道;而且存檔後,政策不必重開就會推送到 進行中的工作階段。

保管機密,不要檢查它

在這樣的一週之後,Bromure 自己架構筆記裡的一行值得重讀:

繞過這道邊界,攻擊者什麼也拿不到,因為這道邊界不是機密被檢查的地方—— 它是機密存在的唯一地方。

閘道是憑證被檢查的地方:一個請求抵達,一個函式檢視它,而那個函式的裁決是 呼叫方與金鑰之間唯一的阻擋。檢查有邊緣情況,也有退路路徑。檢查會掙得一個 CVE、一則 KEV 條目與一個聯邦期限,然後兩個月後在同一個函式的另一條分支上, 再掙得一個。

線路邊界則是憑證所在的地方。沒有東西抵達等著被檢視,因此也沒有裁決可以出錯。 真正的權杖加密躺在一台 Mac 上,工作區握著一個在任何地方都一文不值的 brm- 佔位值,而替換發生在一條只有 hypervisor 寫得進去的通訊端上。

我們在六週內已經把這件事的變奏寫過三次:一個 信任了代理人可以自己寫入的主機名的出向代理、 一個 讀 bash 的方式和 bash 不一樣的命令守衛, 以及 三個編碼代理人,它們的繞過最後都落在某個行程裡的一份真憑證上。 每一次,失守的那個元件都在誠實地做著「評估某件事」的工作,而這正是不變的地方。 評估是比不存在更弱的原語,而我們卻一直把不存在能免費完成的工作,交給評估去做。

把你那些代理人的權限集中起來,是對的決定。請把那個集中起來的東西,放在沒有人 能對它送出請求的地方。安裝 Bromure Agentic Coding, 給每一個代理人一道沒有前門的邊界。