MCPサーバーのセキュリティ問題が、想像以上の速さで現実の脅威になっている。
セキュリティ企業BlueRock Securityが2025年末から2026年初頭にかけて7,000台以上のMCPサーバーを調査したところ、36.7%にSSRF(Server-Side Request Forgery)脆弱性が存在するという結果が出た。きっかけはMicrosoftのMarkItDown MCPサーバーに発見された致命的な欠陥で、悪用されるとAWSアカウントへの完全アクセスすら奪われる可能性がある。
MCPはAIエージェントのデファクトスタンダードになりつつある。それだけに、この統計は開発者として無視できない。この記事では、SSRF脆弱性の仕組みを技術的に解説し、安全なMCPサーバーの実装方法をPythonとNode.jsのコード例とともに紹介する。
何が起きたのか——MarkItDown SSRFとその波及
MicrosoftのMarkItDown MCPサーバーは、URLやファイルパスを受け取ってMarkdown変換を行うツールだ。問題は、このURIに対して一切のバリデーションが行われていない点にある。
BlueRockの研究者David Onwukweが指摘したとおり、「URIに境界がないため、ツールを呼び出すユーザー、エージェント、あるいは攻撃者であっても任意のhttpまたはファイルリソースにアクセスできる」。
具体的には次のような攻撃シナリオが成立する。
| 攻撃ステップ | 内容 | 影響 |
|---|---|---|
| 1. SSRF発動 | http://169.254.169.254/latest/meta-data/にリクエスト |
AWSインスタンスメタデータ取得 |
| 2. IAMロール確認 | メタデータからIAMロール名を取得 | ロール権限の把握 |
| 3. クレデンシャル窃取 | 一時的AWSキー(Access Key + Secret + Token)を取得 | AWSアカウントへのアクセス |
| 4. 権限昇格 | 取得したキーでS3/EC2/IAM等を操作 | 最悪、アカウント完全乗っ取り |
これはMarkItDownだけの問題ではない。BlueRockの調査では、7,000台以上のMCPサーバーのうち36.7%が同様の脆弱性クラスを持つことが判明した。Anthropicの公式Git MCPサーバーにも3件のCVE(CVE-2025-68143, CVE-2025-68144, CVE-2025-68145)が発見されており、MCPエコシステム全体の問題といえる。
MCPサーバーの構築方法や基本概念については、MCPサーバー実践10選でまとめているので、あわせて参照してほしい。
SSRF脆弱性とは——5分で理解する仕組み
SSRF(Server-Side Request Forgery: サーバーサイドリクエストフォージェリ)は、アプリケーションを踏み台にして内部ネットワークや外部サービスに不正なリクエストを送らせる攻撃手法だ。
通常、クラウドの内部エンドポイント(169.254.169.254)はインターネットから直接アクセスできない。しかし、MCPサーバーがEC2上で動作していれば、MCPサーバー自身はそのエンドポイントに到達できる。攻撃者はMCPツールを呼び出してこのアドレスをURIとして渡すだけでよい。
# 攻撃者がMCPクライアント経由で送る悪意のある入力例
# 注意: 本番環境で使用する前に、必ずテスト環境で動作確認してください。
# これは攻撃パターンの理解用コードです
malicious_uri = "http://169.254.169.254/latest/meta-data/iam/security-credentials/"
# MCPサーバーがバリデーションなしでこれをリクエストすると
# AWSの一時クレデンシャルが応答として返ってくる
BlueRockはこれを「氷山問題」と呼んでいる。セキュリティチームはプロンプト層のリクエストに注目しがちだが、エージェントが実際にリソースへアクセスし、ファイルを読み、権限昇格を試みるランタイム層のリスクを見落としやすい、という指摘だ。
まず試したい——安全なMCPサーバー実装3パターン
脆弱性の根本原因は「入力のバリデーション不足」だ。以下に、すぐ実装できる3つの防御パターンを示す。
パターン1: URIホワイトリストバリデーション(Python)
許可するドメインをホワイトリストで管理し、それ以外のリクエストを全て拒否する基本的なアプローチ。
import urllib.parse
from typing import Optional
# 動作環境: Python 3.11+
# 注意: 本番環境で使用する前に、必ずテスト環境で動作確認してください。
ALLOWED_SCHEMES = {"https"}
ALLOWED_DOMAINS = {
"docs.example.com",
"api.example.com",
# 必要なドメインのみを追加
}
# AWS IMDSv1へのアクセスをブロック(特に重要)
BLOCKED_HOSTS = {
"169.254.169.254", # AWS Instance Metadata Service
"metadata.google.internal", # GCP Metadata
"169.254.169.254", # Azure IMDS
"localhost",
"127.0.0.1",
}
def validate_uri(uri: str) -> Optional[str]:
"""
URIをバリデーション。安全なURIのみ返す、それ以外はNoneを返す。
"""
try:
parsed = urllib.parse.urlparse(uri)
except Exception:
return None
# スキームチェック
if parsed.scheme not in ALLOWED_SCHEMES:
return None
# ホスト名チェック(ブロックリスト)
hostname = parsed.hostname or ""
if hostname in BLOCKED_HOSTS:
return None
# IPアドレスの直接指定をブロック(169.x.x.x, 10.x.x.x等)
import ipaddress
try:
ip = ipaddress.ip_address(hostname)
if ip.is_private or ip.is_link_local or ip.is_loopback:
return None
except ValueError:
pass # ホスト名はIPではない(正常)
# ホワイトリストチェック
if hostname not in ALLOWED_DOMAINS:
return None
return uri
# MCPツール実装例
def convert_to_markdown_safe(uri: str) -> str:
safe_uri = validate_uri(uri)
if safe_uri is None:
raise ValueError(f"URI '{uri}' is not allowed")
# 安全なURIのみ処理する
return fetch_and_convert(safe_uri)
ポイント: IPアドレスのprivate/link-local範囲をライブラリ(ipaddress)で判定することで、`169.254.x.x`系のバイパスを防げる。手動の文字列マッチングだけでは`0x6f6f6f6f`のような十六進数表現でのバイパスが成立することがある。
パターン2: Node.jsでのSSRF対策(URL validation)
Node.js環境では`whatwg-url`と`is-ip`を組み合わせて同様の防御を実装できる。
// 動作環境: Node.js 20+
// 必要パッケージ: npm install whatwg-url is-ip ipaddr.js
// 注意: 本番環境で使用する前に、必ずテスト環境で動作確認してください。
import { URL } from 'url';
import ipaddr from 'ipaddr.js';
const ALLOWED_SCHEMES = new Set(['https:']);
const ALLOWED_DOMAINS = new Set([
'docs.example.com',
'api.example.com',
]);
export function validateUri(uri) {
let parsed;
try {
parsed = new URL(uri);
} catch {
throw new Error(`Invalid URI: ${uri}`);
}
// スキームチェック
if (!ALLOWED_SCHEMES.has(parsed.protocol)) {
throw new Error(`Scheme not allowed: ${parsed.protocol}`);
}
const hostname = parsed.hostname;
// IPアドレスチェック
if (ipaddr.isValid(hostname)) {
const addr = ipaddr.parse(hostname);
const range = addr.range();
const blockedRanges = ['private', 'loopback', 'linkLocal', 'multicast'];
if (blockedRanges.includes(range)) {
throw new Error(`Private IP address not allowed: ${hostname}`);
}
}
// ドメインホワイトリスト
if (!ALLOWED_DOMAINS.has(hostname)) {
throw new Error(`Domain not in allowlist: ${hostname}`);
}
return uri;
}
パターン3: MCPサーバー起動時の環境分離(Docker)
コードレベルの対策に加えて、実行環境のネットワーク分離も重要だ。
# docker-compose.yml — MCPサーバーの安全な実行環境
# 動作環境: Docker Compose v2.x
# 注意: 本番環境で使用する前に、必ずテスト環境で動作確認してください。
version: '3.9'
services:
mcp-server:
build: .
networks:
- mcp_restricted
environment:
- NO_PROXY=169.254.169.254,metadata.google.internal # IMDS無効化
# IMDSv1へのアクセスを完全ブロック
extra_hosts:
- "169.254.169.254:127.0.0.2" # IMDSをループバックにリダイレクト
networks:
mcp_restricted:
driver: bridge
driver_opts:
com.docker.network.bridge.enable_ip_masquerade: "false"
ポイント: AWSのEC2では、インスタンスメタデータサービスv2(IMDSv2)を強制し、v1を無効化するのが最善策だ。EC2コンソールまたはAWS CLIで--metadata-options HttpTokens=requiredを設定する。
セキュリティチェックリスト——既存MCPサーバーの点検手順
既存のMCPサーバーを点検する際は、以下のチェックリストを使うとよい。
| チェック項目 | 確認方法 | 対処 |
|---|---|---|
| URIバリデーション | ソースコードでURL/ファイルパスの入力点を探す | ホワイトリスト実装 |
| IMDSv1無効化(AWS) | EC2コンソール > インスタンスメタデータ設定 | HttpTokens=required に変更 |
| IAMロールの最小権限 | IAM > ロール > 付与された権限を確認 | 不要な権限を削除 |
| 外部ライブラリのバージョン | pip show / npm ls で依存パッケージ確認 | 最新版にアップデート |
| ネットワーク分離 | VPCセキュリティグループ、Dockerネットワーク設定を確認 | 必要最小限の通信のみ許可 |
| ログとモニタリング | MCPツール呼び出しのログが記録されているか | CloudWatch / Datadog等でアラート設定 |
Anthropic公式MCPサーバーへのCVE対応
Anthropicの公式Git MCPサーバーに発見された3件のCVEについては、バージョン2025.12.18以降にアップデートすることで対処できる。
# Anthropic Git MCPサーバーのアップデート
# 動作環境: npm/npx
# 注意: 本番環境で使用する前に、必ずテスト環境で動作確認してください。
# 現在のバージョン確認
npm list @anthropic-ai/mcp-server-git
# 最新版にアップデート
npm update @anthropic-ai/mcp-server-git
# または特定バージョンを指定(最低 2025.12.18)
npm install @anthropic-ai/mcp-server-git@2025.12.18
【要注意】よくある失敗パターンと回避策
失敗1: 「ホスト名が`169.254`で始まるかチェック」だけでは不十分
❌ 文字列の前方一致だけでブロック
⭕ ipaddressライブラリでIPの種別を判定(loopback/link-local/private全てをカバー)
なぜ重要か: 攻撃者は0xa9fea9fe(16進表現)や2852039166(10進表現)など、同じIPを別の書き方で渡してバイパスを試みる。ライブラリを使えばこれらを透過的にブロックできる。
失敗2: MCP設定ファイルにAPIキーをハードコード
❌ claude_desktop_config.jsonにAPIキーを直書き
⭕ 環境変数(ANTHROPIC_API_KEY)を使用し、.envはGitignoreに追加
なぜ重要か: 設定ファイルがGitHubに誤ってpushされると、クレデンシャルが即座に公開される。BlueRockの調査でも、公開リポジトリ内のMCPサーバー設定からAPIキーが漏洩したケースが複数確認されている。
失敗3: サードパーティMCPサーバーを無審査で導入
❌ 公開されているMCPサーバーをそのまま本番環境に導入
⭕ コードレビュー → テスト環境検証 → 本番導入のプロセスを必ず踏む
なぜ重要か: MCPエコシステムはまだ黎明期で、公開サーバーの品質にはばらつきがある。BlueRockが運営するMCP Trust Registryでセキュリティ評価を確認することも一つの手段だ。
失敗4: 「プロンプト層」だけ監視して「ランタイム層」を放置
❌ ユーザーの入力だけをログで監視
⭕ MCPサーバーが実際に行ったHTTPリクエスト・ファイルアクセスをランタイムで記録
なぜ重要か: BlueRockが指摘する「氷山問題」そのものだ。セキュリティ侵害はプロンプト層ではなく、エージェントが実際にアクションを起こすランタイム層で発生する。
参考・出典
- MCP fURI: SSRF Vulnerability in Microsoft Markitdown MCP — BlueRock Security(参照日: 2026-03-21)
- Anthropic, Microsoft MCP Server Flaws Shine a Light on AI Security Risks — Security Boulevard(参照日: 2026-03-21)
- Microsoft & Anthropic MCP Servers at Risk of RCE, Cloud Takeovers — Dark Reading(参照日: 2026-03-21)
- MCP Trust Registry — BlueRock Security(参照日: 2026-03-21)
- Microsoft Patches Critical Azure MCP SSRF Vulnerability CVE-2026-26118 — Windows News(参照日: 2026-03-21)
まとめ:今日から始める3つのアクション
- 今日やること: 既存MCPサーバーのコードベースで、URLやファイルパスを受け取る入力点を全て洗い出す。バリデーションがなければ、本記事のホワイトリスト実装を追加する
- 今週中: AWSを使っているならIMDSv2を強制有効化し、IAMロールの権限を最小限に絞る。Anthropic公式MCPサーバーはバージョン2025.12.18以降にアップデートする
- 今月中: MCPサーバーのランタイムログ(HTTPリクエスト・ファイルアクセス)の収集とアラート設定を導入。BlueRockのMCP Trust RegistryでサードパーティMCPサーバーの評価確認を習慣化する
あわせて読みたい:
- 【2026年】MCPサーバー実践10選 — Claude Desktopで即使えるMCPサーバーの設定方法と活用例
- Model Context Protocol(MCP)完全ガイド — MCPの仕組みと実装方法の基礎から応用まで
この記事はAIgent Lab編集部がお届けしました。