powered by TechFeed
表示モード
Deep Dive

マルチテナントMCPサーバーの「テナント間データ分離」、多くの実装でテストが欠落している — キャッシュ汚染から認証情報漏洩まで、実際に壊れる3つのポイント

9月1日、Techstrong.AIが「Multi-Tenant MCP Servers: The Isolation Guarantees Nobody Is Actually Testing」と題した記事を公開した。マルチテナント構成のMCPサーバーにおけるテナント間のデータ分離が実際にはほとんどテストされておらず、深刻な情報漏洩リスクがあることを詳しく論じている。

9月1日、Techstrong.AIが「Multi-Tenant MCP Servers: The Isolation Guarantees Nobody Is Actually Testing」と題した記事を公開した。マルチテナント構成のMCPサーバーにおけるテナント間のデータ分離が実際にはほとんどテストされておらず、深刻な情報漏洩リスクがあることを詳しく論じている。


MCPのマルチテナント問題とは

MCP(Model Context Protocol)は、AIクライアントとツール・データソースを接続するためのオープンプロトコルだ(Anthropicが提唱し、現在は各社が実装している)。もともとは1人のユーザーのAIクライアントを単一バックエンドに接続するために設計された。しかし現在の運用実態は異なる。単一のゲートウェイが複数の内部サーバーを束ねたり、同じインフラを複数の顧客組織で共用するケースが当たり前になっている。

マルチテナントとは、複数の顧客(テナント)が同一のサーバーインフラを共有しながら、互いのデータやツールには触れられないよう論理的に分離されて動作する構成を指す。クラウドSaaSでは標準的なアーキテクチャだが、MCPの文脈ではその分離保証が十分に検証されていないと元記事は指摘する。

問題の核心は、ツールレベルのガバナンス(スキーマ検証、スコープ制限など)が「呼び出し元が1人」という前提に基づいている点だ。マルチテナントに転換した途端、その前提が崩れる。しかも、この種の障害は通常の機能テストでは検出できない。search_ticketsが正しいチケットを返すかどうかのテストは、2テナントが同じキャッシュスロットを競合したときに互いのデータを見てしまうかどうかをテストしない。


実際に壊れる3つのポイント

1. キャッシュキーの誤り(最も頻出)

元記事が指摘する最も典型的な失敗は、キャッシュキーをテナントIDではなくコネクションIDで設定してしまうことだ。

ローンチ当初は問題が出ない。1コネクション=1テナントだからだ。数ヶ月後、レイテンシ改善目的でコネクションプーリングを導入した時点で前提が崩れる。2テナントがコネクションを共用するようになり、最後にキャッシュされたリクエストの結果が別テナントに返ってしまう。

元記事によれば、この問題はCVEとして2件開示されている:

  • CVE-2026-25536:TypeScript SDK v1.10.0〜v1.25.3。並行負荷下でトランスポートまたはサーバーインスタンスを再利用した場合、リクエストしていないクライアントにレスポンスが送られる可能性がある。クライアントとテナントが別々である(通常はそうだ)瞬間に即テナント間漏洩になる。
  • CVE-2026-52869:Python SDK v1.27.1。HTTPトランスポートがセッションIDを無検証で受け入れ、提示したテナントが作成者本人か確認しない。

2つは同じバグの別名ではなく、スタックの異なる層で発生する独立した障害だ。

2. プロキシ認証情報の漏洩

MCPサーバーが他のサービス(DBコネクション、OAuthグラント)の認証情報を代理保持するパターンは一般的だ。問題はテナント解決が静かに失敗したときに起きる。

n8n-MCPはv2.51.2以前のマルチテナントモードでこのバグを抱えていた。テナント解決に失敗すると、リクエストを拒否するのではなくオペレーター自身のインスタンス認証情報を使ってそのまま処理してしまっていた。いわゆるfail-open(失敗時に開く)の典型例だ。

3. ツールメタデータのテナント境界越え

テナントが独自のカスタムツールを登録できるゲートウェイで発生する。tools/listレスポンスのキャッシュがテナントではなくコネクションで管理されている場合、テナントAのツール定義がテナントBのコンテキストに混入する。中身が悪意あるものかどうかは別問題だ。リークそのものがバグであり、コンテンツの内容に関わらず修正すべき対象になる。


なぜ多くの実装でテストが欠落しているのか

元記事はその理由を正直に分析している:

  • 多くのチームは「数顧客向けの実験」としてスタートし、マルチテナントへのスケールアップを事後的に行った
  • セキュリティの議論はプロンプトインジェクションとツール権限に集中しており、クロステナント漏洩は別問題なのに埋もれている
  • 監査はシステム記述に含まれた範囲しかカバーしない。ホスト側のツールレジストリが仕様書に書かれていなければ、誰も「テストしない」という意思決定すらなく、単にテストされない

今すぐできる確認手順

元記事はコードを書く前に、1リクエストのテナントIDが最終的な認証情報にたどり着くまでの経路を紙に書き出すことを勧めている。ほとんどのチームはこれをやったことがなく、それだけで最初のバグを見つけられることが多いという。

元記事が提示する初期テスト2つ:

テスト1(キャッシュ汚染): テナントAのリクエストでキャッシュが温まっている状態で、テナントBのツールリストを取得する。テナントAのツールが返ってきたら、ロードジェネレーター不要でバグが確認できる。

テスト2(セッション検証): テナントCに期限切れのセッションIDを渡し、どの認証情報で処理されるか確認する。この点を意識して設計していない実装では、大抵リクエストが通り、別テナントの認証情報が使われる。

負荷テスト(CVE-2026-25536対応): 2リクエストがキャッシュ書き込みの瞬間に衝突するよう意図的に誘発するフックをコードに埋め込む必要がある。クリーンな結果を信頼するには数百回の反復が必要だ。

マルチテナント分離のテストに使えるツールとして、テナントコンテキストの境界検証にはOWASP Testing Guide(マルチテナント分離の項)が参考になる。また、MCPのセキュリティ上の考慮事項はMCP公式ドキュメントのSecurity sectionでも言及されている。関連する脆弱性情報の追跡にはNIST NVDを活用するとよい。


設計上の原則

元記事の結論として挙げられているのは以下だ:

  • テナントIDはコネクションオブジェクトやクライアントIDから派生させない
  • キャッシュキーには必ずテナントIDを含める
  • 認証情報ルックアップはfail-closed(失敗時に閉じる)で設計する
  • クロステナント分離のテストは機能テストやシングルテナント向けセキュリティテストとは別バジェットで計画する

現時点では、多くのMCPデプロイメントでこのテストが書かれていないまま本番稼働している。


詳細はMulti-Tenant MCP Servers: The Isolation Guarantees Nobody Is Actually Testingを参照していただきたい。