保护您的 MCP 服务器:8 点开发人员清单
2026 年,43 个代理框架附带嵌入式漏洞。在 AI 代理发现漏洞之前,需要进行八项检查来锁定 MCP 服务器。
2026 年 11 月,Barracuda 报告称 43 个代理框架组件附带嵌入式漏洞 通过供应链妥协。 攻击者没有闯入服务器。 他们对人工智能代理的工具下了毒 打电话。 OWASP 代理应用程序前 10 名现在将供应链漏洞和工具滥用列为专门的应用程序 类别。
MCP(模型上下文协议)服务器将您的 API 公开为 AI 助手的可调用工具:Claude Desktop、 光标、VS Code 副驾驶、风帆冲浪。 您注册的每个工具都会成为攻击面。 和大多数 MCP 服务器 出厂时没有身份验证、输入验证和日志记录。
这篇文章是一个用于保护 MCP 服务器安全的 8 点清单。 每个项目都包含您可以使用的工作代码 适应。 这些示例使用 波托伊API 用于模式验证、散列、 和 JWT 解码,但这些模式适用于任何 MCP 服务器。
一张表中的 MCP 安全问题
| 风险 | 会发生什么 | 清单项目 |
|---|---|---|
| MCP 端点上没有身份验证 | 网络上的任何客户端调用任何工具 | #1 添加身份验证 |
| 所有工具都暴露于所有密钥 | 只读监控代理触发破坏性操作 | #2 每个键的范围工具 |
| 没有输入验证 | 代理发送格式错误的 JSON; 工具崩溃或执行意外操作 | #3 使用 Zod 进行验证 |
| 缺少工具注释 | 代理无法区分只读工具和破坏性工具 | #4 设置注释 |
| 无速率限制 | 一个循环代理会在几分钟内耗尽您的 API 配额 | #5 每个代理的速率限制 |
| 没有审计追踪 | 您无法追踪哪个代理导致了生产问题 | #6 记录调用 |
| 工具清单篡改 | 攻击者修改工具定义以重定向呼叫 | #7 Pin 和哈希清单 |
| 没有写入确认 | 代理无需人工审核即可创建、删除或修改数据 | #8 沙盒破坏性操作 |
1. 将身份验证添加到您的 MCP 端点
“它在本地主机上”不是安全模型。 浏览器扩展、本地进程以及运行在其上的任何代码
同一台机器可以达到 localhost:3000。 远程MCP发展迅速; 克劳德桌面,光标,
和 Windsurf 都支持通过 HTTPS 连接到远程 MCP 服务器。
每个请求都需要持有者令牌。 Botoi 的 MCP 服务器位于 api.botoi.com/mcp 接受API
键通过 Authorization 标头。 如果没有有效密钥,您每分钟会突发 5 个请求
每天 100 个; 其一,限制会影响您的计划。
如果您的代理传递 JWT 而不是静态密钥,请对其进行解码以提取范围和身份。 方法如下 从 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 个精选工具。 策展本身就是一种形式 范围界定:仅注册对人工智能助手有意义的工具。 你的服务器应该做同样的事情。
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 Schema。 将架构和代理的输入发送至
/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协议定义了四种注释提示: 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. 每个代理身份的速率限制
陷入重试循环的一个恶意代理不应耗尽您的整个 API 配额。 标准速率限制 仅通过 IP 地址是不够的; 多个代理可以共享相同的IP,单个代理可以轮换IP。
从 API 密钥或 JWT 中提取代理身份,然后使用滑动窗口应用每个身份限制 或者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 的身份验证中间件强制实施两层:突发限制(每分钟 5 个请求)和每日上限 (每天 100 个请求)用于匿名访问。 经过身份验证的密钥根据其身份获得更高的限制 Stripe 订阅层。 将相同的两层模式应用于您的 MCP 服务器。
6. 使用代理属性记录每个工具调用
当生产中出现问题时,您需要回答三个问题:哪个代理调用了哪个代理 工具、时间以及输入内容。 如果没有调用日志,您就是在盲目调试。
对输入进行哈希处理,而不是存储原始参数。 这足以让您进行关联和重复数据删除 不记录敏感数据:
// 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)。 针对异常模式构建警报:单个代理的呼叫量是正常呼叫量的 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, ETC。 |
| 速率限制 | 5 个请求/分钟突发 + 100 个请求/天通过 KV 匿名上限; 每个 API 密钥层的限制更高 |
| 记录 | 每个请求的 Cloudflare Workers 可观测性日志以及代理归属 |
| 体现诚信 | 工具定义采用版本控制的 TypeScript; 通过 CI 部署,没有运行时突变 |
| 破坏性沙箱 | 存储工具(粘贴、短网址、webhook)与只读查找工具分开; 注释信号风险 |
您可以从 Claude Desktop、Claude Code、Cursor、VS Code 或 Windsurf 连接到 Botoi 的 MCP 服务器。 这 MCP 设置页面 有每个客户端的配置。
您的 MCP 服务器安全清单
这是完整的精简清单。 将其打印、固定或粘贴到团队的操作手册中:
- 需要对每个 MCP 端点进行身份验证(承载令牌、API 密钥或 JWT)
- 将每个 API 密钥映射到允许的工具的白名单
- 在执行之前使用 Zod 模式或 JSON 模式验证每个工具输入
- 对每个工具进行注释
readOnlyHint,destructiveHint,idempotentHint, 和openWorldHint - 每个代理身份的速率限制,而不是每个 IP; 同时使用突发上限和每日上限
- 使用代理 ID、工具名称、输入哈希、持续时间和状态记录每个工具调用
- 在构建时对您的工具清单进行哈希处理; 在启动时进行验证并阻止被篡改的部署
- 从破坏性工具返回预览; 执行前需要明确确认
2026 年将有 43 个代理框架附带嵌入式漏洞。MCP 服务器是下一个目标。 上述八项检查不会使您的服务器无懈可击,但它们会缩小攻击者的漏洞 首先利用:开放端点、未经验证的输入和不可见的调用。
FAQ
- MCP 服务器需要身份验证吗?
- 是的。 大多数 MCP 服务器不附带身份验证,这意味着到达端点的任何客户端都可以调用任何工具。 随着远程 MCP 采用的增长,您需要在每个端点上进行 API 密钥或基于 JWT 的身份验证。 仅本地主机并不能保证安全; 本地进程和浏览器扩展也可以到达本地主机。
- 什么是 MCP 工具注释以及它们为何重要?
- 工具注释是 MCP 协议中定义的元数据提示:readOnlyHint、destroyerHint、idempotencyHint 和 openWorldHint。 它们告诉人工智能代理工具是否读取数据、修改状态、是否可以安全重试或联系外部服务。 代理使用这些提示来更安全地决定调用哪些工具以及以什么顺序调用。
- 如何限制 AI 代理调用我的 MCP 服务器的速率?
- 为每个代理(或 API 密钥)分配一个身份并应用每个身份的速率限制。 使用令牌桶或滑动窗口算法。 跟踪 KV 存储或 Redis 中的请求。 Botoi 自己的 MCP 服务器每分钟突发执行 5 个请求,每天执行 100 个匿名访问请求,对经过身份验证的密钥具有更高的限制。
- 我可以使用 Zod 验证 MCP 工具输入吗?
- 是的。 MCP 工具从代理接收任意 JSON。 为每个工具的预期输入形状定义 Zod 架构,并在执行前进行验证。 您可以使用 Botoi /v1/schema/json-to-zod 端点从示例 JSON 生成 Zod 模式,或者像 Botoi 的 schema-builder.ts 那样将 OpenAPI 路径定义转换为 Zod 对象。
- 如何检测对我的 MCP 工具清单的篡改?
- 使用 SHA-256 对您的工具清单进行哈希处理并存储哈希值。 在每个服务器启动或部署之前,重新计算哈希值并进行比较。 如果哈希值不同,则表明有人(或某物)修改了您的工具定义。 您可以通过 Botoi /v1/hash 端点或使用本机加密库计算 SHA-256 哈希值。
开始使用 botoi 构建
150+ 个 API 端点,涵盖查询、文本处理、图片生成和开发者工具。免费套餐,无需信用卡。