MCPについて話しましょう。おそらく耳にしたことがあり、それに関連するセキュリティリスクについても読んだことがあるかもしれません。確かに不安を感じさせる話ではありますが、実際の環境に当てはめてみると、想像をはるかに超える深刻な問題に発展することがあります。
つい先週、私たちのシステムがある不審なChrome拡張機能を検知しました。この拡張機能はlocalhost上のポートにメッセージを送信していました。一見それほど奇妙には見えませんでしたが、詳しく調査したところ、この拡張機能はローカルマシン上で稼働しているMCPサーバーと通信していたことが判明しました。
Chrome拡張機能がMCPと通信できるという事実は、深刻なリスクです。その影響は甚大で、Chromeのサンドボックスモデルのような通常のセキュリティ対策も歯が立ちません。
ファイルシステムへの認証なしのアクセス、そして場合によってはマシンの完全な乗っ取りすら起こり得ます。これは些細な問題ではなく、極めて重大でゲームチェンジャーとなり得る脆弱性です。
さらに厄介なのは、どんなChrome拡張機能でもこの脆弱性を悪用できるという点です。特別な権限は一切必要ありません。脆弱なMCPサーバーがホストマシン上で稼働してさえいれば、それだけで十分なのです。すでに私たちは、ファイルシステムアクセス、Slack、WhatsAppといったサービスに紐づく脆弱なMCPサーバーを発見しています。これはもはや理論上のリスクではなく、現実の脅威であり、その影響は壊滅的なものになりかねません。
ですので、もしローカルでMCPを稼働させているなら、今こそセキュリティを真剣に見直すべきタイミングです。私たちもこれを発見したときは度肝を抜かれましたが、手遅れになる前にすべての人がこの問題に注意を払うべきです。
それでは、心の準備をして、一緒にこの問題を掘り下げていきましょう。
不審なlocalhost接続
ブラウザ拡張機能の動作を監視していたところ、私たちの検知エンジンがlocalhostへネットワークリクエストを送信するChrome拡張機能を検出しました。拡張機能自体には悪意ある挙動の兆候は見られませんでしたが、通信先が私たちの注意を引きました。それはModel Context Protocol(MCP)を実装するローカルサービスと通信していたのです。MCPは、AIエージェントをエンドポイント上のシステムツールやリソースと連携させるためのプロトコルです。
これはすぐに懸念を呼びました。ブラウザ拡張機能がユーザーのマシン上で稼働するMCPサーバーと通信できるのであれば、そのMCPを通じて機密性の高いリソースにアクセスしたり、権限の高い操作を実行したりすることを阻む要素は何もないのではないか、という疑問です。
MCP:デフォルトで開かれた状態
まず、MCPクライアントがMCPサーバーとどのように通信するかを理解しましょう。標準的なトランスポート実装は2種類あります。
- Server-Sent Events(SSE) ― HTTP POSTリクエストを通じてMCPサーバーが通信できるようにする方式。
- 標準入出力(stdio) ― プロセスの標準入力・出力ストリームを通じてMCPサーバーが通信できるようにする方式。
MCPサーバーを実装する際、開発者はサーバーがどのように通信するかを選択できます。サーバーは両方のトランスポート方式に対応することも可能です。
重要なのは、トランスポート層自体には認証機構が実装されておらず、また要求もされていないという点です。アクセス制御を実装するかどうかはMCPサーバー開発者次第ですが、実際には現在のほとんどのMCPサーバーがデフォルトでは認証を強制していません。
ローカル利用の場合、Server-Sent Events(SSE)に依拠するMCPサーバーは通常localhost上のポートにバインドされ、同一マシン上で稼働するプロセス(MCPクライアントなど)からアクセス可能な状態になります。
サンドボックス、そしてハンマーの一撃
ローカルのSSEベースMCPサーバーがどのように動作し、また同一マシン上で稼働するプロセスから一般的にアクセス可能であることを理解したところで、先ほどの疑問に戻りましょう。Chrome拡張機能もこれにアクセスできるのでしょうか。
通信の流れは非常にシンプルです。
- クライアントはサーバーにGETリクエストを送信し、認証なしでセッションIDとメッセージ用エンドポイントを取得します。
- 初期化が完了すると、クライアントはそのメッセージエンドポイントに対してPOSTリクエストを発行し、MCPサーバーが公開している利用可能なツールの一覧を取得したうえで、それらを直接呼び出すことができます。
まず、mcp.soからローカルSSEベースのMCPサーバーをセットアップしました。特に、AIクライアントがローカルファイルシステムと連携できるファイルシステム版を選びました。サーバーのREADMEに記載されたセットアップ手順に従います。
node dist/index.js ~/work/mcp/private_directory
次に、バックグラウンドで動作し、localhost:3001への接続を試みるChrome拡張機能を作成しました。3001番ポートは、ローカルのSSEベースMCPサーバーでよく使われるポートです。もちろんこのポート番号は環境によって異なるため、実際の攻撃で使われる拡張機能であれば、稼働中のMCPインスタンスを見つけるためにローカルポートをスキャンする必要があるでしょう。この拡張機能はサーバー情報を取得し、ツール一覧を取得したうえで、MCPサーバーが提供するツールの呼び出しを試みます。
このChrome拡張機能は、MCPサーバーのツールに対して無制限にアクセスできました。認証は一切不要で、あたかも自分がサーバーの公開機能の一部であるかのようにファイルシステムを操作していました。この影響の大きさは計り知れず、悪意ある悪用や完全なシステム侵害への扉を開くものです。
別のMCPサーバーとの連携も、ほとんど手間をかけずに実現できました。これは、実装の違いに関わらず様々なMCPサーバーと連携するための統一インターフェースを提供するという、このプロトコルの設計目標のおかげです。
私たちはSlack向けのMCPも構築してみましたが、Chrome拡張機能はこれにもアクセスでき、公開されている機能と連携することができました。
このPOC(概念実証)は、Chromeの拡張機能アーキテクチャにおける完全なサンドボックス脱出を実証するものです。Chromeはプライベートネットワークへのアクセスに関するセキュリティ制御を強化してきましたが、ブラウザ拡張機能は依然として顕著な例外として残っています。
2023年、Googleはウェブサイトがユーザーのプライベートネットワーク(localhostや192.168.x.xなど)にアクセスすることを防ぐための、より厳格な対策を導入しました。これは、公開ウェブサイト上で動作する悪意あるスクリプトによって内部インフラが探索・攻撃されることから守るための取り組みでした。
2023年9月:Chrome 117が正式版(Stable)としてリリースされ、非推奨化トライアルが終了しました。Chromeは、公開された非セキュアなコンテキストからのプライベートネットワークへのリクエストをすべてブロックします。
Chrome拡張機能は通常のウェブページと比べて高い権限で動作しますが、それでもChromeのサンドボックス原則に従うよう設計されており、明示的な許可がない限りはオペレーティングシステムやローカルリソースから隔離された状態を保つはずです。
しかし、localhostへの無制限なアクセスはこの隔離の壁を破り、特にローカルMCPサーバーのような公開サービスを通じて、ローカルマシンやより広範な組織環境との予期せぬやり取りを可能にしてしまいます。
混乱を封じ込める
登場して以来、MCPエコシステムは急速に拡大しており、多種多様な機能を提供する数千のサーバーが存在します。これは強力な新たな可能性をもたらす一方で、増大するセキュリティリスクの一群も生み出しています。今回示したように、特別な権限を一切持たないシンプルなChrome拡張機能が、サンドボックスを突破し、ローカルのMCPサーバーに接続して、ユーザーに代わって権限の高い操作を実行できてしまいます。これは、ブラウザのサンドボックスが本来維持すべき隔離モデルを事実上崩壊させるものです。そして、これらのMCPサーバーが認証を強制することなくファイルシステム、Slack、WhatsAppといったツールへのアクセスを公開している場合、そのリスクは理論上の懸念から、組織全体を脅かす脅威へと一気に跳ね上がります。
セキュリティチームにとって、これは単なる新たな攻撃経路ではなく、まったく新しい攻撃対象領域であり、しかもその危険性は大きく過小評価されています。MCPはすでに開発環境や本番システムに導入されており、多くの場合、監督やアクセス制御はほとんど行われていません。放置すれば、従来の防御策を回避してエンドポイントへの裏口を開けたままにしてしまいます。MCPの利用を統制すること、厳格なアクセスポリシーを強制すること、そして拡張機能の挙動を注意深く監視すること――これらはもはや妥協の許されない優先事項とならなければなりません。
翻訳元: https://www.koi.ai/blog/trust-me-im-local-chrome-extensions-mcp-and-the-sandbox-escape