はじめに
株式会社ベーシック 川上です。
昨年12月にAnthropicからMCPが発表され、現在さまざまなMCPサーバーが公開されています。個人でも自由にMCPサーバーを公開することができ、VisualStudio公式でまとめられていたり、AIBaseで公開されていたりします。そんな中、公式ドキュメントでMCPサーバーを利用した「混乱した副官問題」についてセキュリティベストプラクティスが書かれており、MCPサーバーを利用した際のセキュリティ対策について気になったのでまとめようと思います。
MCPの通信プロトコル
MCPサーバーがクライアントと通信を行う際のトランスポート層は以下の2種類を実装することができます。(カスタムトランスポートを実装することもできるようです。)
- stdio(標準入出力)
- ストリーミング可能なHTTP
stdio
stdioを使用する場合、MCPサーバーはクライアントのサブプロセスとして実行されます。MCPサーバーは標準入力(stdin)からメッセージを読み取り、標準出力(stdout)でメッセージを送信します。今回はHTTPがメインとなるので詳細は省きます。
HTTP
このトランスポートでは、MCPサーバーはAPIのように複数のクライアントからの接続が可能な独立したプロセスとして動作します。HTTP POST・GETリクエストを使用し、MCPエンドポイントを提供する必要があります。HTTPを利用したAPIのように、クライアントから呼び出してMCPサーバーにアクセスすることができます。以下のようなMCPサーバーが存在します。
- GitHub MCP Server
- Cloudflare
- Plugged.in
- LiteLLM
stdioを使用したMCPサーバーでは、実行環境から認証に使用する資格情報を取得しますが、HTTPベースのトランスポートを使用したMCPサーバーの認証システムはOAuth2.0, 2.1に基づいています。MCPサーバーはOAuthクライアントとして、MCPサーバーはアクセストークンを使用して保護されたリソースへの要求を受け入れるOAuthリソースサーバーとして機能します。
OAuth認証
ここでOAuth認証について、簡単にまとめておきたいと思います。OAuth(Open Authorization)は、第三者アプリケーションに対して、ユーザーの代わりにリソースへの限定的なアクセス権限を安全に委譲するための標準的な認可フレームワークです。
認証(Authentication)
- 自分が「誰であるか」を証明するプロセス
- 何かによって、対象の真正性を確認する行為を指す。Wikipediaより
- 「IDとパスワードでアプリケーションにログインする」「空港でパスポートを見せて本人確認をする」などが認証のプロセス
認可(Authorization)
- 「誰が」「誰に」「どんな権限」を与えるかを決定する
- ある者に付与された、リソースへのアクセス許可である。また、「認可を付与するプロセス」を「認可」と呼ぶ場合もある。Wikipediaより
- 「ログインしたユーザーは特定のフォルダAにはアクセスできるが、Bにはアクセスできない」「本人確認をした後に発行される搭乗券で、通過できる搭乗ゲートが変わる」など、認証したユーザーごとに権限を振り分けるプロセス
OAuthに関わるロール
- リソースオーナー(Resource Owner):リソースの所有者(通常はユーザー)
- クライアント(Client):リソースにアクセスしたいアプリケーション
- 認可サーバー(Authorization Server):アクセス許可を管理するサーバー
- リソースサーバー(Resource Server):保護されたリソースを提供するサーバー
OAuth認証の例
- リソースオーナー:ユーザー
- クライアント:VSCode
- 認可サーバー:Github
- リソースサーバー:Github
- 認可要求:VSCodeがGitHub(認可サーバー)に対して「ユーザーのリポジトリにアクセスしたい」と要求する
- ユーザー認証:VS Codeが自動的にブラウザを開き、GitHubのログイン画面が表示され、 ユーザー名とパスワードを入力してGitHubにログインする
- 権限の確認:GitHubがVSCodeからアクセスできる権限を表示する
- 認可コード発行:ユーザーが3. を承認すると、GitHubが認可コードを発行してVSCodeにリダイレクトする
- アクセストークン取得:VS Codeが認可コードをアクセストークンに交換し、トークンは暗号化後ローカルに保存される
- リソースアクセス:VS Codeがアクセストークンを使ってGitHub APIを呼び出して各種操作を行う
上記のフローで認証が行われることで、VSCodeはGithubのパスワードを知らずに、ユーザーが承認した機能だけを使用します。Githubの設定画面からVSCodeが使用できる権限をいつでも取り消すことができ、アクセストークンの期限切れ時にも自動で更新されます。
HTTPで通信するMCPサーバーにおける認証
-
初回設定フローの開始
- ユーザーがMCPクライアントにMCPサーバーへの接続を要求
- MCPクライアントがMCPプロキシサーバーに対して外部認証サーバーへのユーザー認証を開始
-
外部認証サーバーへのリダイレクト
- MCPプロキシサーバーが外部認証サーバー(GitHub、Google等)にリダイレクト
- 認証リクエスト(
client_id
、mcp-proxy
等のパラメータを含む)を送信
-
ユーザー認証
- ユーザーが外部認証サーバーでログイン認証を実行
- 認証後、外部認証サーバーがクライアントIDを
mcp-proxy
に含めてコールバック処理を実行
-
認可コード取得
- 外部認証サーバーから認可コード(
mcp-proxy-server.com
へのリダイレクト)を取得 - MCPプロキシサーバーが認可コードを使ってアクセストークンに交換
- 外部認証サーバーから認可コード(
-
MCP認証コードの発行
- MCPプロキシサーバーがMCP認証コードを発行
- MCPクライアントにMCP認証コードでリダイレクト
-
トークン認証コードの交換など
- MCPクライアントとMCPプロキシサーバー間でトークン交換を完了
- 認証済みのセッションが確立
この認証フローでは、MCPプロキシサーバーがMCPクライアントと外部認証サーバーとの仲介を行います。外部認証サーバーとの直接的なOAuth認証を処理し、MCPクライアントにはMCP認証コードを提供します。
混乱した副官問題
「混乱した副官」は、MCPのOAuth認証フローにおけるセキュリティリスクです。この攻撃では、正当なMCPサーバーが攻撃者によって「混乱」させられ、意図しない第三者によるリソースへのアクセスを許可してしまいます。MCPプロキシサーバーが外部認証サーバーから取得した認証情報を、どのMCPクライアントに対して発行すべきか正確に識別できないことが原因です。攻撃者は正当な認証フローに割り込み、本来正当なクライアントに送られるべきMCP認証コードを自身のクライアントにリダイレクトさせることで、ユーザーのリソースへの不正アクセスを行います。
攻撃は以下のフェーズで発生します。
- 認可コード取得
- 攻撃者がリダイレクトURIを操作することで外部認証サーバーからMCPプロキシサーバーへのコールバック時に認可コードを横取りします
- MCP認証コード発行
- ユーザーの認証セッションに便乗して、MCP認証コードを攻撃者自身の制御下にあるクライアントへ転送します
攻撃の流れ
-
攻撃のセットアップ
- 攻撃者が悪意のあるクライアント等を使用してユーザーの認証情報をスキップすることで攻撃を開始
-
偽装されたリダイレクト
- 攻撃者が偽装されたクライアント等を使用して
mcp.proxy-server.com
にリダイレクト - 攻撃リクエスト(
client_id
、mcp-proxy
)や偽装Cookieを送信
- 攻撃者が偽装されたクライアント等を使用して
-
Cookieの不正な利用
- 正当な認証フローで設定された認証Cookieを攻撃者が悪用
- 攻撃者のリンクが実行され、メール、チケット、実物等の情報を不正取得
-
外部認証コードの横取り
- 外部認証サーバーが
mcp-proxy-server.com
へリダイレクト - 攻撃者が外部認証コードを横取り
- 外部認証サーバーが
-
不正アクセス実行
- 攻撃者が外部認証コード等を使用して外部認証サーバーへのトークン交換
- MCP認証コードを発行
- 攻撃者がMCPコード等を
attacker.com
に転用
-
結果
- 攻撃者はMCPコード等をMCPトークンに交換
- 攻撃者のユーザーアクセスとしてすべてのMCPサーバーにアクセス可能
例
対策
MCPサーバー開発者向けの対策
リダイレクトURIの検証を行う
リダイレクトされるURIを事前に定義して置き、完全一致を検証します。文字列の部分一致とワイルドカードの禁止、HTTPSを強制することで想定外のURIへリダイレクトされることを防ぎます。
PKCE(Proof Key for Code Exchange)の実装
PKCEは認可コード横取り攻撃 を防ぐためのセキュリティ対策です。すべてのOAuth実装で推奨されています。クライアントが秘密の文字列(Code Verifier)を生成し、Code VerifierのSHA256ハッシュを作成して認証要求に含め、トークン交換時にはCode Verifierを使用して交換を行います。攻撃者が認可コードを横取りしても、Code Verifierがないためトークン交換をできないようにする仕組みです。
Stateパラメータの実装
OAuth認証フローにおいて CSRF(Cross-Site Request Forgery)攻撃 を防ぐためのセキュリティ対策です。認証開始時にランダム値を生成、セッションへ保存し認証URLに含めます。コールバック時に値を比較し、一致しない場合は認証失敗となります。
まとめ
MCPは便利で今後も使用される機会が増えそうな技術ですが、開発者としても厳格なリダイレクトURI検証やPKCE、Stateパラメータの実装などで技術的な対策を行う必要があります。また、利用者側としても安全なツールのみを使用する、要求された権限の内容をしっかり確認するなど、基本的なことですが対策する必要があります。MCPプロキシサーバーを悪用した混乱した副官攻撃は、「正当なサービス間の信頼関係の悪用」がセキュリティリスクのため、開発者としての技術的対策と利用者としてのセキュリティ意識を高めて使用していきたいです。