MCP サーバーを保護する: 8 項目の開発者チェックリスト
2026 年に、43 のエージェント フレームワークに脆弱性が埋め込まれて出荷されました。AI エージェントがギャップを見つける前に、MCP サーバーをロックダウンするための 8 つのチェック。
2026 年 11 月、バラクーダ社は、43 個のエージェント フレームワーク コンポーネントに脆弱性が埋め込まれた状態で出荷されたと報告しました。 サプライチェーンの妥協によって。 攻撃者はサーバーに侵入しませんでした。 彼らはAIエージェントが使用するツールを汚染した 電話する。 OWASP Agentic Apps トップ 10 では、サプライ チェーンの脆弱性とツールの誤用が専用としてリストされるようになりました カテゴリー。
MCP (Model Context Protocol) サーバーは、AI アシスタントの呼び出し可能なツールとして API を公開します: Claude Desktop、 カーソル、VS Code 副操縦士、ウィンドサーフィン。 登録したすべてのツールが攻撃対象領域となります。 そしてほとんどの MCP サーバー 認証、入力検証、ログ記録なしで出荷されます。
この投稿は、MCP サーバーをセキュリティで保護するための 8 項目のチェックリストです。 各項目には、実行可能なコードが含まれています。 適応する。 例では、 Botoi API スキーマ検証、ハッシュ、 および JWT デコードですが、パターンはどの MCP サーバーにも当てはまります。
1 つのテーブルにおける MCP セキュリティ問題
| リスク | 何が起こるのですか | チェックリスト項目 |
|---|---|---|
| MCP エンドポイントに認証がありません | ネットワーク上のすべてのクライアントが任意のツールを呼び出します | #1 認証を追加する |
| すべてのキーに公開されるすべてのツール | 読み取り専用の監視エージェントが破壊的な操作をトリガーする | #2 キーごとのスコープ ツール |
| 入力検証なし | エージェントは不正な形式の JSON を送信します。 ツールがクラッシュしたり、意図しない操作が実行されたりする | #3 Zod で検証する |
| ツールの注釈がありません | エージェントは読み取り専用ツールと破壊的なツールを区別できない | #4 アノテーションを設定する |
| レート制限なし | 1 つのループ エージェントが数分で API クォータを使い果たす | #5 エージェントごとのレート制限 |
| 監査証跡なし | どのエージェントが本番環境の問題を引き起こしたかを追跡できない | #6 ログ呼び出し |
| ツールマニフェストの改ざん | 攻撃者がツール定義を変更して通話をリダイレクトする | #7 ピンとハッシュのマニフェスト |
| 書き込みの確認なし | エージェントは人間によるレビューなしでデータを作成、削除、または変更します | #8 サンドボックスの破壊的な作戦 |
1. MCP エンドポイントに認証を追加します。
「ローカルホスト上にある」というのはセキュリティモデルではありません。 ブラウザ拡張機能、ローカル プロセス、および上で実行されているコード
同じマシンが到達できる localhost:3000。 リモート MCP は急速に成長しています。 クロード デスクトップ、カーソル、
および Windsurf はすべて、HTTPS 経由のリモート MCP サーバーへの接続をサポートしています。
すべてのリクエストで Bearer トークンを要求します。 Botoi の MCP サーバー: api.botoi.com/mcp APIを受け入れる
キーを介して Authorization ヘッダ。 有効なキーがないと、1 分あたり 5 件のリクエストがバーストされます
そして1日あたり100。 1 つあれば、制限は計画に合わせて拡張されます。
エージェントが静的キーの代わりに JWT を渡す場合は、それをデコードしてスコープと ID を抽出します。 その方法は次のとおりです MCP リクエスト ヘッダーから JWT を検査するには:
curl -s -X POST https://api.botoi.com/v1/jwt/decode \\
-H "Content-Type: application/json" \\
-d '{
"token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhZ2VudC1jdXJzb3ItNDIiLCJzY29wZSI6InJlYWQ6ZG5zIHJlYWQ6c3NsIiwiaWF0IjoxNzE3MDAwMDAwfQ.signature"
}'
{
"success": true,
"data": {
"header": { "alg": "RS256", "typ": "JWT" },
"payload": {
"sub": "agent-cursor-42",
"scope": "read:dns read:ssl",
"iat": 1717000000
},
"signature": "signature"
}
}
の sub クレームによりエージェントが特定されます。 の scope クレームには、どのツールが使用できるかがリストされています
アクセスします。 これらの値を信頼する前に、サーバー側の署名を確認してください。
2. API キーごとのツールのスコープ
すべてのエージェントがすべてのツールを必要とするわけではありません。 DNS レコードをチェックする監視エージェントは、決してアクセスしてはなりません。 ペースト作成エンドポイント。 各 API キーを特定のツール セットにマップする許可リストを作成します。
// Scope tools per API key using an allowlist
interface AgentPermissions {
allowedTools: Set<string>;
maxBurst: number;
dailyLimit: number;
}
const AGENT_PERMISSIONS: Record<string, AgentPermissions> = {
"key_readonly_monitor": {
allowedTools: new Set(["lookup_dns", "lookup_ssl", "lookup_ip"]),
maxBurst: 10,
dailyLimit: 500,
},
"key_full_access": {
allowedTools: new Set(["*"]), // wildcard for all tools
maxBurst: 30,
dailyLimit: 5000,
},
};
function canUseTool(apiKey: string, toolName: string): boolean {
const perms = AGENT_PERMISSIONS[apiKey];
if (!perms) return false;
if (perms.allowedTools.has("*")) return true;
return perms.allowedTools.has(toolName);
}
Botoi の MCP サーバーは、150 以上の API エンドポイントから 49 の厳選されたツールを公開します。 キュレーション自体が形式である スコープの設定: AI アシスタントにとって意味のあるツールのみが登録されます。 サーバーも同じことを行う必要があります。
3. Zod スキーマを使用してツール入力を検証する
MCP ツールは、エージェントからの任意の JSON を受け入れます。 エージェントが送信できるのは あなたはどこで
文字列を期待するか、ツールが認識しないフィールドを含めます。 実行前にすべての入力を検証します。
Botoi の MCP サーバーは、起動時に次を使用して OpenAPI パス定義を Zod スキーマに変換します。
schema-builder.ts。 の buildZodSchema 関数は OpenAPI 仕様を読み取り、マップします
各プロパティ タイプを Zod タイプに設定し、必須フィールドにマークを付けます。
import { z } from "zod";
import { buildZodSchema } from "./schema-builder";
// Build a Zod schema from your OpenAPI spec for each tool
const dnsSchema = z.object(buildZodSchema("/v1/dns/lookup", "post"));
function validateToolInput(toolName: string, input: unknown) {
const schemas: Record<string, z.ZodTypeAny> = {
lookup_dns: dnsSchema,
// ... one schema per tool
};
const schema = schemas[toolName];
if (!schema) {
throw new Error(\`Unknown tool: \${toolName}\`);
}
const result = schema.safeParse(input);
if (!result.success) {
return {
error: "Invalid input",
issues: result.error.issues.map((i) => ({
path: i.path.join("."),
message: i.message,
})),
};
}
return { data: result.data };
}
サンプル入力から Zod スキーマを生成する
OpenAPI 仕様がない場合は、サンプル ツール入力から Zod スキーマを生成します。
curl -s -X POST https://api.botoi.com/v1/schema/json-to-zod \\
-H "Content-Type: application/json" \\
-d '{
"json": {
"domain": "github.com",
"check_mx": true,
"timeout_ms": 5000
}
}'
{
"success": true,
"data": {
"schema": "z.object({ domain: z.string(), check_mx: z.boolean(), timeout_ms: z.number() })"
}
}
実行時に JSON スキーマに対して入力を検証する
Zod を使用しない実行時検証には、JSON スキーマを使用します。 スキーマとエージェントの入力をに送信します。
/v1/schema/validate:
curl -s -X POST https://api.botoi.com/v1/schema/validate \\
-H "Content-Type: application/json" \\
-d '{
"schema": {
"type": "object",
"required": ["domain"],
"properties": {
"domain": { "type": "string", "minLength": 1 },
"record_type": { "type": "string", "enum": ["A", "AAAA", "MX", "TXT", "CNAME", "NS"] }
},
"additionalProperties": false
},
"data": { "domain": "stripe.com", "record_type": "MX" }
}'
有効な入力は次を返します。
{
"success": true,
"data": {
"valid": true,
"errors": []
}
}
エージェントが合格した場合 "record_type": "INVALID"、ツールを実行する前に対処可能なエラーが発生します。
{
"success": true,
"data": {
"valid": false,
"errors": [
{
"path": "/record_type",
"message": "must be equal to one of the allowed values"
}
]
}
}
4. ツールの注釈を設定する
MCP プロトコルでは、次の 4 つの注釈ヒントが定義されています。 readOnlyHint、 destructiveHint、
idempotentHint、 そして openWorldHint。 これらは、ツールがデータを読み取るかどうかをエージェントに伝えます。
状態を変更するか、再試行しても安全か、または外部サービスに連絡します。 ほとんどの MCP サーバーはそれらを無視します。
Botoi の MCP サーバーは、49 個のツールすべてに注釈を付けます。 実際には次のようになります。
// curated-tools.ts (from Botoi's MCP server)
export const CURATED_TOOLS: Record<string, CuratedTool> = {
lookup_dns: {
path: "/v1/dns/lookup",
method: "post",
title: "DNS Lookup",
description: "Query DNS records for a domain.",
annotations: {
readOnlyHint: true, // reads data, changes nothing
openWorldHint: true, // contacts external DNS servers
},
},
storage_paste_create: {
path: "/v1/paste/create",
method: "post",
title: "Create Paste",
description: "Store text content and return a short URL.",
annotations: {
destructiveHint: true, // creates new data
idempotentHint: false, // each call creates a new paste
},
},
};
注釈を尊重するエージェントは、確認なしに破壊的なツールを呼び出すことを避け、優先します。 失敗したリクエストを再試行するときの冪等ツール。 すべてのツールに設定します。
| 注釈 | 意味 | 例 |
|---|---|---|
readOnlyHint: true |
ツールはデータを読み取り、何も変更しません | DNS ルックアップ、SSL チェック、IP 地理位置情報 |
destructiveHint: true |
ツールはデータを作成、更新、または削除します | ペーストの作成、短縮 URL の生成、Webhook の送信 |
idempotentHint: true |
同じ入力で複数回呼び出しても安全 | ハッシュ生成、JSONフォーマット、単位変換 |
openWorldHint: true |
ツールが外部サービスに接続する | DNS over HTTPS、WHOIS、URL メタデータ抽出 |
5. エージェント ID ごとのレート制限
1 つの不正エージェントが再試行ループに陥って API クォータ全体を使い果たさないようにする必要があります。 標準レート制限 IP アドレスによるだけでは十分ではありません。 複数のエージェントが同じ IP を共有でき、単一のエージェントが IP をローテーションできます。
API キーまたは JWT からエージェント ID を抽出し、スライディング ウィンドウを使用して ID ごとの制限を適用します。 または KV に保存されたトークン バケット:
// Hono middleware for per-agent rate limiting
import type { Context, Next } from "hono";
const BURST_LIMIT = 5; // requests per minute
const DAILY_LIMIT = 100; // requests per day
async function rateLimitAgent(c: Context, next: Next) {
// Extract agent identity from API key or JWT
const apiKey = c.req.header("Authorization")?.replace("Bearer ", "");
const agentId = apiKey || c.req.header("X-Forwarded-For") || "anonymous";
// Check burst limit (sliding window in KV)
const burstKey = \`rate:\${agentId}:burst\
Botoi の認証ミドルウェアは、バースト制限 (1 分あたり 5 リクエスト) と 1 日あたりの上限の 2 つの層を強制します。 (1 日あたり 100 リクエスト) 匿名アクセス。 認証されたキーには、そのキーに基づいてより高い制限が適用されます。 Stripe サブスクリプション層。 同じ 2 層パターンを MCP サーバーに適用します。
6. エージェントの属性を使用してすべてのツール呼び出しをログに記録する
運用環境で何か問題が発生した場合、どのエージェントがどのエージェントに電話をかけたかという 3 つの質問に答える必要があります。 ツール、いつ、どのような入力を使用したか。 呼び出しログがなければ、盲目的にデバッグすることになります。
生の引数を保存する代わりに入力をハッシュします。 これにより、関連付けと重複排除を行うのに十分な情報が得られます。 機密データをログに記録せずに:
// Log every tool invocation with agent identity
interface ToolInvocationLog {
timestamp: string;
agent_id: string;
tool_name: string;
input_hash: string; // SHA-256 of the input (not the raw input)
duration_ms: number;
status: "success" | "error";
}
async function logToolInvocation(
agentId: string,
toolName: string,
input: unknown,
startTime: number,
status: "success" | "error"
) {
const inputStr = JSON.stringify(input);
const inputHash = await crypto.subtle.digest(
"SHA-256",
new TextEncoder().encode(inputStr)
);
const hashHex = Array.from(new Uint8Array(inputHash))
.map((b) => b.toString(16).padStart(2, "0"))
.join("");
const log: ToolInvocationLog = {
timestamp: new Date().toISOString(),
agent_id: agentId,
tool_name: toolName,
input_hash: hashHex,
duration_ms: Date.now() - startTime,
status,
};
// Send to your logging pipeline (Datadog, Loki, Cloudflare Logpush)
console.log(JSON.stringify(log));
}
これらの構造化ログを既存のパイプライン (Datadog、Grafana Loki、Cloudflare Logpush) に送信します。 異常なパターンに対するアラートを作成します。1 人のエージェントが通常の 10 倍の通話量を発生する場合や、読み取り専用のエージェントが発生する場合などです。 エージェントが破壊的なツールを呼び出そうとしています。
7. ツール定義を固定し、整合性をチェックする
Barracuda のレポートでは、攻撃者が悪意のあるツール定義をエージェント フレームワークに挿入していることが判明しました。 侵害された依存関係。 誰かが MCP ツール マニフェスト (名前、説明、パラメータ) を変更した場合 スキーマ)を使用すると、エージェントは意図したものとは異なる動作でツールを呼び出します。
ビルド時にツール マニフェストをハッシュします。 起動時にハッシュを再計算して比較します。
import crypto from "node:crypto";
import Botoi from "@botoi/sdk";
const botoi = new Botoi();
// Your tool manifest (the source of truth)
const tools = [
{ name: "dns_lookup", path: "/v1/dns/lookup" },
{ name: "ip_lookup", path: "/v1/ip/lookup" },
{ name: "email_validate", path: "/v1/email/validate" },
// ... all your tools
];
const manifest = JSON.stringify(tools);
// Hash it at build time and store the expected hash
const EXPECTED_HASH = "a3f2b8c1d4e5f6..."; // from your CI build
// At startup, verify integrity
function verifyManifestIntegrity() {
const currentHash = crypto
.createHash("sha256")
.update(manifest)
.digest("hex");
if (currentHash !== EXPECTED_HASH) {
throw new Error(
\`Tool manifest tampered. Expected \${EXPECTED_HASH}, got \${currentHash}\`
);
}
console.log("Tool manifest integrity verified.");
}
// Or use the Botoi API for environments without Node crypto
async function verifyWithApi() {
const result = await botoi.hash.sha256({ input: manifest });
if (result.data.hash !== EXPECTED_HASH) {
throw new Error("Tool manifest tampered.");
}
}
Botoi API を使用してハッシュを生成する
CI 環境またはエッジ ランタイムの場合 node:crypto:
curl -s -X POST https://api.botoi.com/v1/hash \\
-H "Content-Type: application/json" \\
-d '{
"text": "[{\\"name\\":\\"dns_lookup\\",\\"path\\":\\"/v1/dns/lookup\\"},{\\"name\\":\\"ip_lookup\\",\\"path\\":\\"/v1/ip/lookup\\"}]",
"algorithm": "sha256"
}'
{
"success": true,
"data": {
"hash": "a3f2b8c1d4e5f67890abcdef1234567890abcdef1234567890abcdef12345678",
"algorithm": "sha256"
}
}
期待されるハッシュを環境変数として保存します。 デプロイ時に比較してください。 ハッシュが異なる場合、 展開をブロックします。
8. サンドボックスの破壊的操作
データの書き込み、副作用のトリガー、または外部サービスへの接続を行うツールには、確認フローが必要です。 人間によるレビューなしにエージェントに 1,000 個のペーストを作成させたり、Webhook を起動させたりしないでください。
// Confirmation flow for destructive MCP tools
interface ToolResult {
content: Array<{ type: string; text: string }>;
isError?: boolean;
}
function handleDestructiveTool(
toolName: string,
input: Record<string, unknown>,
confirmed: boolean
): ToolResult {
const destructiveTools = new Set([
"paste_create",
"short_url_create",
"webhook_inbox_create",
]);
if (!destructiveTools.has(toolName)) {
// Non-destructive: execute immediately
return executeTool(toolName, input);
}
if (!confirmed) {
// Return a preview instead of executing
return {
content: [{
type: "text",
text: JSON.stringify({
action: "confirmation_required",
tool: toolName,
input,
message: \`This tool will create new data. Pass "confirmed": true to proceed.\`,
}),
}],
};
}
// Confirmed: execute the destructive operation
return executeTool(toolName, input);
}
パターンは単純です。破壊ツールは最初の呼び出し時にプレビューを返します。 エージェントが示します
ユーザーにプレビューを表示します。 ユーザーが確認した後でのみ、エージェントは次のコールを送信します。
"confirmed": true。
外部サービスに接続するツールの場合 (openWorldHint: true)、追加を検討してください
タイムアウトとサーキットブレーカー。 外部 API が遅いか停止している場合、MCP サーバーは
応答を待って永遠にハングアップします。
Botoi の MCP サーバーがこれらのチェックを適用する方法
Botoi の MCP サーバー: api.botoi.com/mcp これは作業上の参照として機能します
チェックリスト。 各項目がどのようにマッピングされるかは次のとおりです。
| チェックリスト項目 | ボトイのやり方 |
|---|---|
| 認証 | API キー経由 Authorization ヘッダ; 厳格なレート制限のある匿名アクセス |
| ツールの範囲設定 | 150 以上のエンドポイントから厳選された 49 のツール。 エージェントに適したツールのみが登録されます |
| 入力の検証 | schema-builder.ts OpenAPI スキーマを Zod に変換します。 実行前に検証します |
| ツールの注釈 | あらゆるツールが curated-tools.ts もっている readOnlyHint、 destructiveHint、など。 |
| レート制限 | 5 リクエスト/分バースト + 100 リクエスト/日匿名キャップ (KV 経由)。 API キー層ごとの制限の上限 |
| ロギング | エージェントの属性を含むすべてのリクエストの Cloudflare Workers 可観測性ログ |
| 明示的な整合性 | ツール定義はバージョン管理された TypeScript で行われます。 実行時の変更なしで CI 経由でデプロイされる |
| 破壊的なサンドボックス化 | ストレージ ツール (貼り付け、短縮 URL、Webhook) は、読み取り専用の検索ツールとは別のものです。 注釈はリスクを知らせます |
Botoi の MCP サーバーには、Claude Desktop、Claude Code、Cursor、VS Code、または Windsurf から接続できます。 の MCP設定ページ 各クライアントの設定があります。
MCP サーバーのセキュリティ チェックリスト
完全なチェックリストをここにまとめました。 印刷するか、固定するか、チームの Runbook に貼り付けます。
- すべての MCP エンドポイント (ベアラー トークン、API キー、または JWT) で認証を要求する
- 各 API キーを許可されたツールの許可リストにマッピングする
- 実行前に Zod スキーマまたは JSON スキーマを使用してすべてのツール入力を検証します
- すべてのツールに注釈を付ける
readOnlyHint、destructiveHint、idempotentHint、 そしてopenWorldHint - IP ごとではなく、エージェント ID ごとのレート制限。 バーストキャップとデイリーキャップの両方を使用する
- すべてのツール呼び出しをエージェント ID、ツール名、入力ハッシュ、期間、ステータスとともに記録します。
- ビルド時にツールマニフェストをハッシュします。 起動時に検証し、改ざんされた展開をブロックする
- 破壊的なツールからプレビューを返します。 実行前に明示的な確認が必要
2026 年に、43 のエージェント フレームワークに脆弱性が埋め込まれて出荷されました。MCP サーバーが次のターゲットです。 上記の 8 つのチェックはサーバーを無敵にするものではありませんが、攻撃者が侵入する隙を埋めることにはなります。 最初に悪用します: オープンエンドポイント、未検証の入力、および目に見えない呼び出し。
FAQ
- MCP サーバーには認証が必要ですか?
- はい。 ほとんどの MCP サーバーは認証なしで出荷されるため、エンドポイントに到達するクライアントはすべてのツールを呼び出すことができます。 リモート MCP の導入が進むにつれて、すべてのエンドポイントで API キーまたは JWT ベースの認証が必要になります。 Localhost-only はセキュリティを保証するものではありません。 ローカル プロセスとブラウザ拡張機能も localhost に到達できます。
- MCP ツールのアノテーションとは何ですか?なぜ重要ですか?
- ツールの注釈は、MCP プロトコルで定義されているメタデータ ヒントです (readOnlyHint、destructiveHint、idempotentHint、openWorldHint)。 これらは、ツールがデータを読み取るか、状態を変更するか、再試行しても安全か、または外部サービスに接続するかどうかを AI エージェントに伝えます。 エージェントはこれらのヒントを使用して、どのツールをどの順序で呼び出すかについてより安全な決定を下します。
- MCP サーバーを呼び出す AI エージェントをレート制限するにはどうすればよいですか?
- 各エージェント (または API キー) に ID を割り当て、ID ごとのレート制限を適用します。 トークン バケットまたはスライディング ウィンドウ アルゴリズムを使用します。 KV ストアまたは Redis でリクエストを追跡します。 Botoi 独自の MCP サーバーは、匿名アクセスに対して 1 分あたり 5 件のバースト要求と 1 日あたり 100 件の要求を強制し、認証されたキーの制限を高めます。
- Zod を使用して MCP ツールの入力を検証できますか?
- はい。 MCP ツールはエージェントから任意の JSON を受け取ります。 各ツールの予期される入力形状の Zod スキーマを定義し、実行前に検証します。 Botoi /v1/schema/json-to-zod エンドポイントを使用してサンプル JSON から Zod スキーマを生成したり、Botoi の schema-builder.ts のように OpenAPI パス定義を Zod オブジェクトに変換したりできます。
- MCP ツール マニフェストの改ざんを検出するにはどうすればよいですか?
- ツール マニフェストを SHA-256 でハッシュし、そのハッシュを保存します。 各サーバーの起動または展開前に、ハッシュを再計算して比較します。 ハッシュが異なる場合は、誰か (または何か) がツール定義を変更したことになります。 SHA-256 ハッシュは、Botoi /v1/hash エンドポイント経由、またはネイティブ暗号ライブラリを使用して計算できます。
botoiで開発を始めよう
150以上のAPIエンドポイント。検索、テキスト処理、画像生成、開発者ユーティリティに対応。無料プラン、クレジットカード不要。