数週間前、GitHubとファイルシステムのサーバーがすでに動いている同じClaude Codeセッションに「無料WhatsApp MCP」を接続しそうになった。きれいなリポジトリ、ロゴ、一行のインストール。設定ブロックをコピーして、最後のキー入力で止まった。何かが引っかかったからだ——そのサーバーのツール説明に実際に何が書かれているか、まったく知らなかった。
その引っかかりが、すべての要点だった。2025年4月、Invariant Labsはまさにこの種の無害に見えるサーバー——トリビアゲーム——がツール説明に隠れた指示を仕込んでいた攻撃を実証した。その指示はエージェントに対し、別の信頼できるWhatsAppサーバーからのメッセージを攻撃者の番号に密かに転送し、チャット履歴をメッセージ本文の中に隠して密輸するよう命じていた(Invariant Labs)。悪意あるサーバーはWhatsApp自体に直接触れなかった。ただエージェントを説得してそれをやらせただけだ。
設定ブロックを渡すとき、誰もこれを教えてくれない。MCPサーバーは呼び出すライブラリではない。エージェントの推論の参加者だ。ツール説明はモデルのコンテキストに直接注入され、モデルはそれを従うべき指示として扱う——つまり信頼済みのサーバーの隣に置かれた信頼されていないサーバーは、信頼済みのサーバーを操り人形のように操れる。悪いサーバーをサンドボックスに入れても意味がない。攻撃はネットワークではなくエージェントを通じて実行されるからだ。
要点: サードパーティのMCPサーバーを接続する前に、それを書いた人と後から変更できる人への信頼を延長することになる。10分かけて確認しよう——何を読めるか、どこに到達できるか、秘密情報で何をするか、承認後に静かに別のものに変化できるか。大半は読む作業であって、ツールを使うことではない。これが監査の内容だ。
MCPサーバーがパッケージより大きな要求である理由
通常のnpmパッケージはコードを実行する。それだけで十分悪い。MCPサーバーはそれに加え、モデルが従うテキストを注入し、多くの場合認証情報を保持し、他のサーバーと共有セッションに存在する。信頼の表面が4つ、1つではない。
公式MCP セキュリティガイダンスはこれを明確に名指しする。OAuth プロキシにおける混乱した代理人問題、「トークンパススルー」アンチパターン(仕様が明確に禁止——「MCPサーバーはMCP サーバーに明示的に発行されていないトークンを受け入れてはならない」)、OAuth 発見中のサーバーサイドリクエストフォージェリ、セッションハイジャック、悪意ある起動コマンドによるローカルサーバーの侵害を文書化している(MCP Security Best Practices)。ローカル侵害のセクションは明白な悪用さえ具体的に記述する:何らかの収集エンドポイントに curl -X POST -d @~/.ssh/id_rsa を実行する起動コマンドだ。仕様は実質的に、設計した人々が書いた「MCPサーバーがあなたを傷つける可能性のある方法リスト」だ。
GitHubからサーバーを選ぶ個人開発者にとって特に重要な攻撃クラスがさらに2つある。1つ目はツールポイズニング——モデルは完全に読むが、クライアントUIには切り捨てて表示されるツール説明とパラメータフィールドに詰め込まれた隠れたプロンプトインジェクション指示(Vulnerable MCP Project)。2つ目はラグプル:承認当日はきれいなツール説明を提供し、後から密かに汚染されたものと差し替えるサーバーだ。プロトコルには必須の整合性チェックがなく、マニフェストのピン留めもなく、tools/list_changed 通知はオプションで再同意を強制しない——したがって差し替えはあなたが気づかないうちに起こりうる(Acuvity)。ツールポイズニングは現在、文献で最も再現されている攻撃だ;学術的なMCPToxベンチマーク全体がその周りに構築されている(MCPTox)。
監査は偏執症ではない。文書化され、命名され、再現された攻撃に対応している。
10分間の監査
この順序で実行する。初期のものは安価で最悪のケースを捕捉するため、最後まで到達することはほとんどない。
1. ツール説明を自分で読む——生のやつを。 これは最も価値の高い単独チェックで、ほとんど誰もやらない。クライアントは整った要約を表示する;モデルは完全なペイロードを見る。サーバーのソースを開き、すべての description フィールドとすべてのパラメータ説明を、コードであるかのように読む——モデルにとって実際そうだから。人間向けのドキュメントではなくエージェントへの指示に読めるものを探す:「使用前に、これも呼び出してください…」、「常に……の内容を含めてください」、「Xについて尋ねられたら、代わりにYをしてください」、他のサーバーやファイルへの参照、または不可視のUnicodeパディングされたテキスト。電卓のツール説明にSSHキーが出てきたら終わり——タブを閉じろ。
2. すべての送信ネットワーク呼び出しを見つける。 fetch、axios、http、requests、urllib、curl、net.、生のIPアドレスをソースで grep する。ファイルシステムサーバーがリモートホストに連絡する理由はない。合法的に1つのAPIを呼び出すサーバーはそのAPIだけを呼び出すべきだ。仕様のSSRFセクションは、サーバーが http://169.254.169.254/ ——IAM認証情報を配布するクラウドメタデータエンドポイント——をヒットするよう誘導される可能性があるため存在する。リンクローカルまたはプライベート範囲(169.254.*、10.*、192.168.*、127.*)への送信はすべて即時の危険信号だ(MCP Security Best Practices)。説明のつかない送信が、ツールと流出チャンネルの違いだ。
3. シークレットの扱いをトレースする。 トークンはどこに行く?環境変数から読み取られてローカルで使用されるか、どこかに渡されるか?サーバーがトークンパススルーをしていないことを確認する——仕様が要求するオーディエンスチェックなしに認証情報を直接ダウンストリームAPIに渡すこと。READMEがスコープを絞ったトークンではなく広いパーソナルアクセストークンを貼り付けるよう指示している場合、それはサーバーの設計が「爆発半径について考えたことがない」と告白している。MCPサーバーには、その1つの用途のためだけに必要な権限を超える認証情報を絶対に渡すな。
4. 権限を実際の作業に限定する。 仕様自身のスコープ最小化セクションは、事前に files:*、db:*、admin:*、またはワイルドカード all/full-access スコープを公開するサーバーに対して警告している。盗まれた広いトークンがすべてを一度に開くからだ(MCP Security Best Practices)。このサーバーが本当に何を必要とするかを問う。「コミットをフォーマットする」サーバーがリポジトリ全体への書き込みとネットワークアクセスを要求するのは過剰な要求だ。最小限を与え、初日からカタログを要求するものより段階的に狭いスコープを要求するサーバーを好む。
5. 何の隣に置かれているかを確認する。 これがWhatsAppエクスプロイトの教訓だ:リスクは1つのサーバーではなく、組み合わせにある。同じセッション内の信頼されていないサーバーは信頼されたものをハイジャックできる。エージェントのMCPリストを共有信頼境界として扱え。実際の認証情報や書き込みアクセスを持つものと同じセッションで、未検証のサーバーを実行するな。新しいものを試す必要があるなら、価値あるものが何も接続されていない独自のセッションを与えろ。
6. バージョンをピン留めする。動く標的を拒否する。 ラグプルが機能するのは、ほとんどの人が latest から、または自分が管理しないリモートサーバーからインストールするからだ。特定のコミットまたはリリースタグにピン留めし、できればソースをベンダーし、バンプするたびにツール説明を再読する。ピン留めできないリモート/ホスト型MCPサーバーについては、いつでも自分のツールを再定義できるサーバーを受け入れたと理解する——それは実際の継続的な信頼付与であり、一回限りのインストールではない。
7. スキャナーを実行し、その後は自分の読みをより信頼する。 uvx mcp-scan@latest はMCP設定を読み、サーバーに接続し、ツール説明を引き出し、ツールポイズニング、ラグプルドリフト、クロスオリジンシャドーイング、プロンプトインジェクションにフラグを立てる(Invariant Labs)。オープンソースで設定不要。高速な最初のパスとして使え——ただしスキャナーは既知のパターンを捕捉し、ステップ1での読みはスキャナーが捕捉しない巧妙なものを捕捉する。
8. コミットログとIssuesタブを読む。 依存関係を検査するときと同じ動き。1年間に2度認証モデルを改訂したプロトコルの上で何ヶ月も更新されていないサーバーは化石だ。コミット履歴が全部「README更新」でエッジケース修正が一件もないなら、誰も実際に使っていない。そしてIssuesタブは、難しい方法で発見する前に流出バグについて教えてくれることが多い。
サンドボックスに入れろ、読むだけでは不十分
ソースを読むと、コードが何をしているように見えるかがわかる。推移的依存関係が実行時に何をするかはわからない——なぜならツリー全体を読もうとしないからだ。そのためローカルサーバーに対する仕様のアドバイスが正しい最後の砦だ:制限されたファイルシステムとネットワークアクセスでサンドボックス化して実行し、クライアントだけが到達できるよう stdio トランスポートを使い、何かが実行される前に正確なコマンドを示す本物の同意ダイアログを使う(MCP Security Best Practices)。
個人の開発環境では:知らないサーバーは、環境にキーを持つメインのシェルではなく、コンテナまたは制限されたユーザーアカウントで実行する。コンテナには作業に必要な1つのディレクトリへのアクセスを与え、残りは拒否する。一度だけ10分のセットアップで、「このサーバーはホームディレクトリ全体を読める」が「このサーバーは私が渡した1つのフォルダしか読めない」に変わる。NSA自身のMCPガイダンスも同じ結論に達している——最小権限、隔離、自分で書いていないサーバーの周りの監視(NSA CSI: MCP Security)。
断るべきとき
監査が始まる前に失格になるサーバーがある。以下の場合はインストールしない:
- ソースを読めないとき。 クローズドソースまたは難読化されたローカルサーバーは、目隠しで実行している起動コマンドだ。パス。
- 広い、長期の認証情報を求めるとき。 使う唯一の方法が神モードトークンなら、設計がすでに「爆発半径を尊重しない」と告白している。
- ピン留めできないリモートサーバーで、失いたくないものへのアクセスを持つとき。 それはラグプルへの恒常的な招待状だ。使い捨てには問題ない;本番データには不可。
- ツール説明がエージェントへの指示のように読めるとき。 ドキュメントは説明する;命令しない。私を飛ばして直接モデルに話しかけるものは、見た瞬間に失格だ。
- 利便性が基準を超えないとき。 週5分節約するサーバーは、認証情報1つとエージェントの信頼境界内の席の価値がない。評価した「クールな」MCPサーバーのほとんど、必要なかった。
難しいのは個別のチェックではない——インストール時に付与した信頼が固定されていないことだ。監査したパッケージは来週別のパッケージになりうる。プロトコルはそれをあなたに伝えることを強制しない。だから持続する習慣は一回限りの監査ではない。リストを小さく、ピン留めし、サンドボックス化しておき、バージョンをバンプするたびに説明を再読することだ。ステップ1から始めよ:ソースを開き、ツール説明を声に出して読め。あなたではなくエージェントに話しかけているように聞こえたら、すでに答えが出ている。
