MCP 安全最佳实践:威胁、修复方案与检查清单
基于 MCP 官方规范的安全最佳实践:提示词注入、工具投毒、混淆代理、令牌透传、SSRF 和本地服务器风险,逐一给出缓解措施,并附上可直接使用的检查清单。
一段话讲清 MCP 安全最佳实践
MCP 安全最佳实践可以归结为三条规则:把服务器发来的一切(工具描述、工具结果、OAuth URL)都当作不可信输入;给每个服务器和令牌尽可能小的权限;凡是会写入、发送或删除的操作,都要让人参与确认。官方的 MCP Security Best Practices 页面和 授权规范(当前修订版 2026-07-28)把这些规则落成了具体的 MUST 和 SHOULD 要求。本文把每一种威胁对应到服务器作者、客户端用户和组织各自的修复方案,最后给出一份检查清单。
威胁地图速览
| 威胁 | 出了什么问题 | 谁先修 |
|---|---|---|
| 通过工具输出的提示词注入 | 工具返回的网页、issue 或邮件里夹带了模型会执行的指令 | 客户端和用户 |
| 工具投毒与 rug pull | 工具描述里藏着隐藏指令,或已批准的工具后来被改掉 | 客户端和组织 |
| 混淆代理(Confused Deputy) | 使用静态 OAuth client ID 的 MCP 代理让攻击者绕过用户授权确认 | 服务器作者 |
| 令牌透传(Token Passthrough) | 服务器接受或转发了并非签发给自己的令牌 | 服务器作者 |
| 权限范围过宽 | 一个被盗令牌就能解锁所有工具 | 服务器作者和授权服务器 |
| 恶意本地服务器 | 一键安装在你的机器上执行任意命令 | 客户端、用户和组织 |
| OAuth 发现过程中的 SSRF | 恶意服务器把客户端引向内部 URL | 客户端作者 |
| 状态句柄劫持 | 被猜中的购物车或工作流 ID 暴露了其他用户的数据 | 服务器作者 |
后六行直接来自规范的安全页面。前两行并不是协议漏洞,而这恰恰是它们危险的原因:它们利用的是模型,而不是传输格式。
通过工具输出的提示词注入
每个工具结果最终都会进入模型的上下文。如果一个工具抓取了 GitHub issue、网页或收件箱,那么内容的作者就可以尝试操纵模型,例如让它读取私有仓库并把内容发到公开的地方。Invariant Labs 在 2025 年就针对使用 GitHub MCP 集成的 Agent 演示过这种模式。
规范的 tools 章节 对双方都写得很明确:
- 服务器 MUST 校验所有工具输入、实现访问控制、对调用限流,并清洗工具输出。
- 客户端 SHOULD 对敏感操作弹出确认、在调用服务器前向用户展示工具输入、在把工具结果交给模型前做校验、设置超时并记录工具调用日志。
- 出于信任与安全考虑,SHOULD 始终有一个可以拒绝工具调用的人在环中。
实用的缓解措施:
- 读写服务器分离。 读取不可信内容(浏览网页、issue、邮件)的 Agent,在同一会话中不应持有能推送代码或发送邮件的令牌。
- 对有副作用的操作保持确认。 只读工具可以自动批准;写入、删除、支付和外发消息应保留确认提示。
- 把令牌限定在单个仓库或工作区。 如果 GitHub MCP Server 只能看到一个仓库,注入的指令就碰不到其他仓库。
- 对浏览器类工具保持警惕。 Playwright MCP Server 会读取任意页面,除非确有需要,请在不含登录态的浏览器配置文件中运行它。
工具投毒、遮蔽与 Rug Pull
工具描述是模型会读、但用户通常看不到的提示词文本。Invariant Labs 在 2025 年 4 月的披露 中描述了三种变体:
- 工具投毒(Tool Poisoning): 一个看似无害的工具(比如
add函数)暗藏指令,让模型读取 SSH 密钥或配置文件,再通过某个参数偷偷带出去。 - 遮蔽(Shadowing): 恶意服务器的描述改变了模型使用另一个可信服务器工具的方式,比如悄悄更改邮件收件人。
- Rug Pull: 你已经批准的服务器后来修改了它的工具定义。
规范自身的立场是:除非来自可信服务器,客户端 MUST 把工具注解(例如只读或破坏性提示)视为不可信。缓解措施:
- 展示完整描述。 客户端应展示模型实际看到的内容,而不是一个简化标签。
- 锁定版本并对定义做哈希。 在配置中锁定包版本(用
npx -y some-server@1.4.2,而不是@latest),工具定义哈希变化时重新审查。 - 按服务器给工具加命名空间。 规范指出,聚合多个服务器的客户端 SHOULD 对重名工具做区分,例如加上服务器前缀。
- 少装一点。 每个已连接的服务器都会往模型上下文里加文本。不用的服务器就断开。
混淆代理(MCP 代理服务器)
如果你构建的 MCP 服务器用一个静态 OAuth client ID 对接第三方 API,同时又允许 MCP 客户端动态注册,就需要关注这一点。攻击者用自己的 redirect_uri 注册一个客户端,给受害者发一个链接;第三方授权服务器看到已有的授权 cookie 就跳过了确认页面,授权码最终落到攻击者的服务器上。
规范要求 MCP 代理服务器在转发到第三方授权服务器之前实现按客户端的授权确认:
- 为每个用户维护已批准的
client_id登记表,并先检查它。 - 展示由 MCP 服务器自己提供的确认页面,写明客户端名称、第三方权限范围和确切的
redirect_uri,并带有 CSRF 防护以及frame-ancestors或X-Frame-Options: DENY。 - 授权 cookie MUST 使用
__Host-前缀,设置Secure、HttpOnly、SameSite=Lax,经过签名,并绑定到具体的client_id。 - 用精确字符串匹配校验
redirect_uri。 - 生成一次性、短时效的
state值,并且只在用户批准确认之后才存储它。
令牌透传与受众校验
令牌透传是指服务器不检查令牌是否签发给自己就接受客户端的令牌,再把它转发给下游。规范明确禁止:MCP 服务器 MUST NOT 接受任何并非明确签发给它的令牌。这种做法会破坏限流和审计链路,并让一个被盗令牌变成跨服务的通行证。
对服务器作者:
- 用服务器的规范 URI 校验每个令牌的受众(
aud声明或内省结果)。 - 如果要调用上游 API,就单独获取一个独立的上游令牌。绝不转发客户端的令牌。
- 除非明确是多租户,否则固定到单一 issuer 或租户。
完整流程,包括把令牌绑定到单个服务器的 resource indicators,请看我们的 MCP 认证与 OAuth 指南。
权限范围过宽
一个一开始就被授予 files:*、db:* 和 admin:* 的令牌,一旦从日志里泄露,影响面会非常大。规范给出的权限最小化建议:
- 从最小权限集开始,例如只覆盖低风险读取操作的
mcp:tools-basic。 - 在首次使用特权工具时,通过有针对性的
WWW-Authenticate质询(带error="insufficient_scope"的403)申请更多权限。 - 避免使用
*、all或full-access这类通配或大一统权限,也不要在scopes_supported中列出所有可能的权限。 - 在服务器端按工具检查权限;仅凭 scope 声明并不等于授权逻辑。
对用户来说,同样的思路也适用于 API Key:每个服务器单独建一个 key,如果工作流只需要只读,就只给只读权限。
恶意或被攻陷的本地服务器
本地(stdio)服务器是一个以你的用户权限运行的程序。规范中恶意启动命令的例子就包括把 ~/.ssh/id_rsa 发送到远程 URL。如果客户端支持一键安装本地服务器,它 MUST 展示完整、不截断的命令,标明这是代码执行,并要求用户明确批准。客户端 SHOULD 高亮危险模式(sudo、rm -rf、网络调用),并以最小默认权限在沙箱中运行服务器。
你今天就能做的:
- 批准前先读命令。 优先选择来自知名发布者的服务器,并检查任何收录条目背后的代码仓库,包括 mcp.so 上的条目。
- 限制文件系统访问范围。 Filesystem MCP Server 通过参数指定允许访问的目录;传入项目文件夹,绝不要传整个用户主目录。
- 容器化。 在容器中运行服务器,例如通过 Docker MCP Gateway,可以限制被攻陷的服务器能接触到的东西。
- 服务器作者: 本地服务器优先使用 stdio。如果必须在本地监听 HTTP,请要求授权令牌或使用 Unix domain socket,因为 DNS 重绑定可以访问未受保护的 localhost 端口。
SSRF 与恶意 OAuth URL
在 OAuth 发现过程中,客户端会请求由服务器控制的 URL:resource_metadata URL、authorization_servers 列表以及授权服务器元数据里的各个端点。恶意服务器可以把它们指向 169.254.169.254(云元数据)、localhost:6379 或私有网段。客户端作者 SHOULD 要求 HTTPS(仅在开发环境允许回环地址使用 http),屏蔽私有和链路本地 IP 段,校验每一跳重定向,并考虑使用出口代理。规范提醒不要手写 IP 解析,因为攻击者会利用八进制、十六进制和 IPv4 映射 IPv6 等编码技巧。
授权 URL 本身也是输入。客户端 MUST 只允许 http 和 https 协议,拒绝 javascript:、data: 和 file:,并且 MUST NOT 通过 cmd.exe 或 sh 这样的 shell 打开 URL。
状态句柄与混淆攻击(Mix-Up)
2026-07-28 修订版是无状态的:协议层没有会话,需要保存状态的服务器会生成一个句柄(购物车 ID、工作流 ID),并作为工具参数接收它。服务器 MUST NOT 把持有句柄当作身份认证。用安全随机数生成器生成句柄、设置过期时间,并把它绑定到从已验证令牌中取出的用户 ID。与多个授权服务器打交道的客户端 MUST 记录预期的 issuer,并在兑换授权码之前校验授权响应中的 iss 参数(RFC 9207),以此挡住单靠 PKCE 挡不住的 mix-up 攻击。
MCP 安全检查清单
服务器作者
- 校验并清洗所有工具输入和输出;对工具调用限流。
- 远程 HTTP 服务器按规范实现 OAuth;绝不接受或转发并非签发给你的令牌。
- 设计最小权限范围和逐步提权质询;按工具执行授权检查。
- 代理服务器:按客户端授权确认、精确匹配
redirect_uri、一次性state。 - 使用不可猜测、绑定用户的状态句柄;绝不凭句柄认证。
- 工具描述保持简短、真实,不包含对模型的指令;变更要显式版本化。
客户端用户
- 从知名发布者安装服务器,锁定版本,读一遍启动命令。
- API Key 和 OAuth 授权都限定在最小范围;每个服务器一个 key。
- 对写入、删除和外发消息保持确认。
- 能避免就避免在同一会话中混用处理不可信内容的工具和高权限工具。
- 断开不再使用的服务器;轮换曾经粘贴进配置文件的 key。
组织
- 维护已批准服务器及版本的白名单;审查工具定义的变更。
- 在容器或沙箱中运行本地服务器,限制文件系统和网络访问。
- 把凭据存放在密钥管理系统中,并以环境变量注入,而不是写进提交到仓库的配置文件。
- 集中记录工具调用日志,对异常调用量或异常目的地告警。
- 上线前用 MCP Inspector 测试服务器,并确认团队使用的 客户端 是否支持 OAuth。
下一步
从你已经在用的服务器开始:确认每个服务器的凭据放在哪里、带有哪些权限范围、写操作是否需要确认。然后在 MCP 服务器目录 中寻找官方或由发布者维护的替代品;如果你在构建远程服务器,请阅读 MCP 认证的工作原理。
常见问题
MCP 安全吗?
- 协议本身规定了安全要求,比如远程服务器使用 OAuth 2.1、校验令牌受众以及用户授权确认,但是否安全取决于具体实现。大部分实际风险来自不可信的服务器、通过工具输出进行的提示词注入以及权限过大的令牌,所以要审查服务器,并让人来确认敏感操作。
什么是 MCP 工具投毒?
- 工具投毒是指恶意 MCP 服务器在工具描述中隐藏指令,模型会读到这些指令,但用户通常看不到。与之相关的 rug pull 是指已批准的服务器后来修改了工具定义,因此要锁定版本,并在工具定义变化时重新审查。
MCP 中的令牌透传是什么?
- 令牌透传是一种反模式:MCP 服务器接受了并非签发给自己的令牌,并把它转发给下游 API。MCP 规范明确禁止这样做:服务器必须校验令牌是签发给自己的,并且必须为上游 API 单独获取令牌。
运行本地 stdio MCP 服务器安全吗?
- 本地 stdio 服务器以你的用户权限运行,它的安全程度取决于你批准的代码和命令。请从知名发布者安装服务器,阅读完整的启动命令,锁定版本,并在限制文件和网络访问的容器或沙箱中运行。