正しいAPIキーを持っていない相手が、社内AIにつないだMCPツールを呼べてしまう。
LiteLLMの旧版で確認された認証迂回は、任意のBearerトークンでMCPセッションを確立できるものだった。[1]
しかもCISAは、2026年9月2日にこの脆弱性を、実際の悪用が確認されたKEVカタログへ追加している。 [2]
LiteLLMは、複数のLLM APIへの呼び出しをまとめるゲートウェイとして使われる。本記事のproxyは、その中継サーバーを指す。
2026年9月9日、Wizは「Off Guard」と題した詳細調査を公開した。[3] 読んでいて立ち止まるのは、もう一つの問題だ。AIの入出力を検査するためのCustom Code Guardrailsにも、サーバー側のコード実行につながる穴があった。 [4]
APIの入口も、安全確認の仕組みも、攻撃面になる。
この記事では、二つの穴がどこで成立したのか、どこまで被害が届くのか、運用担当者が今日何を確認すべきかを掘り下げる。
対象は影響を受ける旧版と、その機能・公開設定がそろった環境です。両脆弱性には修正版があります。9月9日はWizの詳細記事の公開日で、未修正のゼロデイがこの日に発見されたという意味ではありません。
また、MCPの認証迂回から、そのままguardrailのコード実行へ進めるわけではありません。 別々の経路として確認します。
まず確認する二つの番号
運用担当者は、最初に実際に稼働している版と、外部から到達できる機能を確認してほしい。
| 確認項目 | MCPの認証迂回 | Custom Code Guardrailsのコード実行 |
|---|---|---|
| CVE | CVE-2026-59822 | CVE-2026-59821 |
| 公式の影響範囲 |
1.84.0 未満 |
1.82.0-stable 未満 |
| 当該問題の修正版 | 1.84.0 |
1.82.0-stable |
| 入口 | 到達可能なMCP Streamable HTTPエンドポイント | guardrailの作成・更新経路 |
| 主な前提 | 有効なLiteLLMキーなしでも認証処理を通過できる旧実装 | 作成・更新経路に到達できる権限や設定。master key未設定時の管理者扱いも影響 |
| 到達する範囲 | 設定されたMCPツールと、その接続先 | LiteLLM proxyの実行環境と、プロセスが利用できる秘密情報 |
版と影響条件はBerriAIの公式アドバイザリによる。[1][4] 1.82.0-stable の接尾辞も原文どおり記載した。この表は各問題が修正された境界を示している。いま更新する際は、その後の修正も含む保守中のリリースを確認する。
「管理画面にはログインが必要だった」「いつもの推論APIでは401が返った」。その確認だけで、MCPの入口まで検査したことにはならない。
認証が失敗した。そのあとに通していた
MCP側の処理は、LiteLLMのキーと、接続先MCPサーバーへ渡すOAuth2トークンの両方を扱っていた。[5]
ここには二種類の責任がある。
- LiteLLMが、自分の利用者を認証する。
- 接続先サービスが、自分に渡されたトークンを検証する。
問題の実装では、LiteLLMキーの検証が失敗したとき、OAuth2向けのフォールバックで空の UserAPIKeyAuth() が作られた。結果として、無効な認証情報を持つ要求がMCPツールへ到達できた。[5]
これは認証情報の正しさを証明した流れではない。認証に失敗した事実が、別の処理に進む途中で失われていた。 図は脆弱な経路を整理したもので、全MCPサーバーが同じ権限を持つという意味ではない。
修正コミット 73869f0 では、要求が対象とするサーバーのすべてについて、運用者が auth_type=oauth2 を設定している場合にフォールバックを限定した。対象が特定できない場合や、OAuth2以外のサーバーが含まれる場合は認証失敗を維持する。[5]
この修正から読み取れる設計上の教訓は、例外処理にも明確な許可条件が必要だということだ。
「この鍵は自分には検証できない」と「別の検証者へ渡してよい」は、独立して判定する必要がある。後者を、前者の失敗だけから推測してはいけない。
MCPの先に何を置いたかで、被害が変わる
たとえば、ある構成では読み取り専用の社内検索しか呼べない。一方、別の構成ではチケットの更新や業務処理を実行するツールが公開されているかもしれない。
これは被害範囲を考えるための構成例だ。今回の調査で、これらすべての業務が実際に侵害されたと確認されたわけではない。
確認したいのは、MCPツールの名前、操作内容、接続先の資格情報、アクセス可能なデータの組み合わせである。認証迂回の影響を、モデルの回答が盗み見られる程度に縮めて考えると、書き込み可能なツールを見落とす。
AIを守るコードが、proxyで動いていた
もう一つのCVEは、Pythonで検査処理を書けるCustom Code Guardrailsに関係する。
公式アドバイザリによると、テスト用エンドポイントと、本番の作成・更新経路で、コードの検証と実行制限が一致していなかった。 作成・更新できる利用者のコードがproxy環境で実行され、プロセスが持つ秘密情報の露出につながり得た。[4]
ここで見たいのは、画面の「テスト成功」という表示より、そのあとにコードを読み込む経路だ。
修正後の実装を読むと、初期化時に _compile_custom_code() が呼ばれ、その中で検証、実行環境の設定、compile() と exec() が行われる。[6] _compile_custom_code() という名前でも、内部では exec() でコードを実行している。
関数の外に書かれた処理は、関数を呼ぶ前に動く。 「まだLLMへの推論リクエストを流していない」は、コードが一度も実行されていない証拠にならない。
空の辞書を渡しただけでは、Pythonの組み込み関数は消えない
ここは小さな実験で確認できる。以下は外部通信もファイル操作もしない、Pythonの名前解決だけを観察するコードだ。
source = "result = len([10, 20, 30])"
scope = {}
exec(source, scope)
print("empty globals:", scope["result"])
restricted_scope = {"__builtins__": {}}
try:
exec(source, restricted_scope)
except NameError as exc:
print("empty builtins:", type(exc).__name__)
出力はこうなる。
empty globals: 3
empty builtins: NameError
Pythonは、exec() に渡されたglobals辞書に __builtins__ がなければ、組み込み関数の辞書を補う。[7] 最初のケースで len() が使えるのはそのためだ。
そして、ここで話を終えると危ない。
__builtins__ を空にするだけで、信用できないPythonを安全に実行できるわけではない。 Pythonの公式ドキュメントも、この上書きをセキュリティ機構として扱わないよう明記している。[7]
修正コードには、共有の検証処理、組み込み関数の制限、guardrail管理エンドポイントの PROXY_ADMIN 確認が入っている。[6][8] この個別修正と、任意コードを隔離するシステム全体の設計は、別々に評価したい。
自分たちで類似機能を作るなら、入力検査に加えて、実行プロセスの権限、渡す秘密情報、アクセスできるファイル、外向き通信を設計する必要がある。検査用のコードに、本番proxyと同じ能力を渡す理由があるかを問うべきだ。
なお、今回のRCEの説明対象はproxyコンテナ内の実行と、そのプロセスから届く範囲である。ホストOSのroot権限や、クラウドアカウント全体の掌握が自動的に付いてくるわけではない。
2026年2月、公開環境の約1割がサンプル鍵か認証なしだった
Wizは2026年2月のインターネット公開環境の調査について、次の数値を公表した。[3] 表中のサンプル鍵は sk-1234 を指す。
| 観測内容 | 台数(全体に対する割合) |
|---|---|
| 発見した公開インスタンス | 3,074(100%) |
| サンプル鍵が通る、または認証不要 | 294(約9.6%) |
| 上の294台のうち認証不要 | 191(約6.2%) |
191台は294台の内数だ。足して485台にはならない。
9.6%は 294 ÷ 3,074 × 100 を丸めた観測値である。現在の全LiteLLM環境の割合でも、294台すべてでコード実行に成功したという数字でもない。
ここで統計上気をつけたいのは、標本の選ばれ方だ。
インターネットから発見できる環境には、公開設定などの偏りがある。台数を増やしたり、二項分布の信頼区間を付けたりしても、その偏りまでは補正できない。この数値から「あなたの会社が危険である確率は9.6%」とは計算できない。
それでも、サンプルの鍵や認証なしの配置が、公開環境で実際に観測されたという事実は残る。[3] 自分の環境では、割合を推測するより設定を直接確認するほうが早い。
今日確認することは、この順番
以下は、公開された脆弱性と修正内容を踏まえた運用上の確認案だ。検証は自分が管理する環境で行う。
1. 稼働中の版と、MCPの到達経路を押さえる
リポジトリの依存ファイルに書かれた版だけで判断しない。実際のコンテナやデプロイの版、イメージのdigest、起動しているレプリカを確認する。
古いレプリカが一つ残っていれば、ロードバランサー越しの確認結果が毎回同じになるとは限らない。更新完了の証拠は、稼働実体にひも付けて残す。
更新までの暫定対処として、BerriAIはMCPルートの無効化、またはreverse proxyやAPI gatewayでの遮断を示している。[1] 不要な公開経路を閉じ、正規利用者が必要とする経路を洗い出す。
2. master keyの実効設定を確認する
公式ドキュメントでは、master keyをProxy Adminの鍵と位置付け、設定ファイルまたは LITELLM_MASTER_KEY での設定を説明している。サンプルには sk-1234 が登場する。[9]
サンプルの鍵は秘密にならない。 環境ごとに生成した強い秘密値を使い、一般のアプリに管理用の鍵を配っていないか確認する。
環境変数が存在するかだけで終えず、設定ファイル、デプロイ時の注入、実行中の設定まで照合する。調査のために秘密値そのものをログへ出力する必要はない。
3. guardrailの登録履歴と、実行権限を確認する
管理APIを誰が呼べるか、現在登録されているカスタムコードは誰がいつ追加したものか、承認された内容と一致しているかを確認する。
見覚えのないコードがある場合、変更履歴やログを保存し、到達可能だった資格情報と接続先を調べる。更新作業で新しい侵入を防いでも、過去に露出した秘密が無効になるわけではない。
侵害が疑われる環境では、調査と封じ込めを進め、必要な資格情報の失効・再発行と信頼できる状態からの再構築を判断する。
4. 認証の正常系と失敗系を、両方試す
自社の検証環境で、管理APIとMCPを分けて確認すると、見落としを減らせる。
| 検証対象 | 確認したい結果 |
|---|---|
| 非OAuth2のMCP接続先に無効な資格情報 | 認証失敗が維持され、ツールへ到達しない |
| 接続先を特定できないMCP要求 | 匿名で許可されない |
| 正規のOAuth2連携 | 正当な要求が動き、無効な上流トークンが接続先で拒否される |
| 一般利用者によるguardrail管理 | 管理者限定の操作が拒否される |
| guardrailのテストと登録 | 双方で共通のコード検証が適用される |
| 更新後の各レプリカ | 同じ修正版と意図した設定で動く |
修正差分が示す失敗条件を、実際の配置に合わせて確認するための表である。[5][6][8] HTTPステータスに加え、バックエンド側でツールの処理や管理操作が実行されていないことも確かめる。
この記事からは断定できないこと
CISAのKEV追加で確認できるのは、MCP側のCVE-2026-59822について実悪用の証拠があることだ。[2] すべての構成で、guardrailのRCEやクラウド侵害まで実行されたという証拠にはならない。
また、本記事では第三者の公開環境をスキャンしたり、脆弱なLiteLLMに攻撃コードを送ったりしていない。掲載したPythonの小実験は、exec() の名前解決を確認するものだ。
被害範囲の判断には、その環境の版、公開経路、管理権限、登録済みツール、プロセスの資格情報、ログが必要になる。
「ガードレールあり」のチェックボックスで終わらせない
AI基盤のレビューに、次の三行を追加したい。
- 認証に失敗した要求は、すべての入口で止まるか。
- 検査用コードを登録する操作は、誰に許されているか。
- そのコードが動く場所に、どの権限と秘密情報を渡しているか。
モデルが危険な回答を拒否できても、管理APIに認証の穴があれば、その防御は間に合わない。
AIを守る仕組み自身を、攻撃されるソフトウェアとして点検する。 今回の二つのCVEは、その確認を後回しにできない理由になる。
参考リンク
情報確認日:2026年9月13日。本文中の [番号] は以下に対応する。
[1] BerriAI — MCP Authentication Bypass via OAuth2 Passthrough Fallback(GHSA-7488-6r32-c95q)
[2] CISA — Adds Seven Known Exploited Vulnerabilities to Catalog(2026年9月2日、公式配信)
[3] Wiz — Off Guard: Breaking LiteLLM from authentication bypass to cloud compromise(2026年9月9日)
[4] BerriAI — Custom Code Guardrails production endpoints bypass code safety checks(GHSA-72m8-9m7m-h278)
[5] BerriAI — MCP認証のフォールバック条件を修正したコミット 73869f0
[6] BerriAI — 修正コミットe50b448時点のCustomCodeGuardrail実装
[7] Python — Built-in Functions: exec
[8] BerriAI — guardrail管理権限と検証処理の修正コミット e50b448
[9] LiteLLM — Virtual Keys