引言
目的和范围
本文档提供了模型上下文协议 (MCP) 的安全考虑因素,作为 MCP 授权 规范的补充。本文档识别了特定于 MCP 实现的安全风险、攻击向量 和最佳实践。 本文档的主要受众包括实施 MCP 授权流程的开发者、MCP 服务器运营商以及评估基于 MCP 系统的安全 专业人员。本文档应与 MCP 授权规范以及 OAuth 2.0 安全最佳实践 一起阅读。攻击和缓解措施
本节详细介绍了针对 MCP 实现的攻击,以及潜在的对策。混淆代理问题
攻击者可以利用连接第三方 API 的 MCP 代理服务器,造成 “混淆代理” 漏洞。此攻击允许恶意客户端通过利用静态客户端 ID、动态客户端注册和 同意 Cookie 的组合,在未经适当用户同意的情况下获取 授权码。术语
MCP 代理服务器 : 一种将 MCP 客户端连接到第三方 API 的 MCP 服务器,提供 MCP 功能,同时委托操作并作为单个 OAuth 客户端连接到第三方 API 服务器。 第三方授权服务器 : 保护第三方 API 的授权服务器。它可能缺乏 动态客户端注册支持,要求 MCP 代理对所有请求使用 静态客户端 ID。 第三方 API : 提供实际 API 功能的受保护资源服务器。访问此 API 需要由 第三方授权服务器颁发的令牌。 静态客户端 ID : MCP 代理服务器与第三方授权服务器通信时使用的 固定 OAuth 2.0 客户端标识符。此客户端 ID 指的是作为第三方 API 客户端的 MCP 服务器。对于所有 MCP 服务器到第三方 API 的交互,无论哪个 MCP 客户端发起请求,该值都是相同的。易受攻击的条件
当存在以下所有条件时,此攻击成为可能:- MCP 代理服务器与第三方 授权服务器使用 静态客户端 ID
- MCP 代理服务器允许 MCP 客户端 动态注册(每个 客户端获得自己的 client_id)
- 第三方授权服务器在首次授权后设置 同意 Cookie
- MCP 代理服务器在转发到第三方授权之前未实施适当的每客户端同意
架构和攻击流程
正常 OAuth 代理使用(保留用户同意)
恶意 OAuth 代理使用(跳过用户同意)
攻击描述
当 MCP 代理服务器使用静态客户端 ID 与 第三方授权服务器进行认证时,以下攻击成为 可能:- 用户通常通过 MCP 代理服务器进行认证以访问 第三方 API
- 在此流程中,第三方授权服务器在用户代理上设置一个 Cookie ,表明同意该静态客户端 ID
- 攻击者随后向用户发送一个包含精心制作的授权请求的恶意链接,其中包含恶意重定向 URI 以及新动态注册的客户端 ID
- 当用户点击链接时,他们的浏览器仍然拥有来自之前合法请求的同意 Cookie
- 第三方授权服务器检测到 Cookie 并跳过 同意屏幕
- MCP 授权码被重定向到攻击者的服务器
(在 动态客户端注册 期间的恶意
redirect_uri参数中指定) - 攻击者将窃取的授权码交换为 MCP 服务器的访问 令牌,而无需用户的明确批准
- 攻击者现在可以作为被泄露的用户访问第三方 API
缓解措施
为了防止混淆代理攻击,MCP 代理服务器 必须 实施 每客户端同意和适当的安全控制,如下详述。同意流程实现
下图显示了如何正确实施每客户端同意,该同意在第三方授权流程 之前 运行:所需的保护措施
每客户端同意存储 MCP 代理服务器 必须:- 维护每个用户批准的
client_id值注册表 - 在发起第三方 授权流程 之前 检查此注册表
- 安全地存储同意决定(服务器端数据库,或服务器 特定 Cookie)
- 按名称清晰标识请求的 MCP 客户端
- 显示请求的特定第三方 API 范围
- 显示将发送令牌的已注册
redirect_uri - 实施 CSRF 保护(例如 state 参数、CSRF 令牌)
- 通过
frame-ancestorsCSP 指令或X-Frame-Options: DENY防止 iframe 嵌入,以防止点击劫持
- 对 Cookie 名称使用
__Host-前缀 - 设置
Secure、HttpOnly和SameSite=Lax属性 - 进行加密签名或使用服务器端会话
- 绑定到特定的
client_id(不仅仅是“用户已同意”)
- 验证授权请求中的
redirect_uri与注册的 URI 完全 匹配 - 如果
redirect_uri更改而未重新注册,则拒绝请求 - 使用精确字符串匹配(而不是模式匹配或通配符)
state 参数对于防止授权码
拦截和 CSRF 攻击至关重要。适当的 state 验证确保
授权端点的同意批准在回调端点得到执行。
实施 OAuth 流程的 MCP 代理服务器 必须:
- 为每个授权请求生成加密安全的随机
state值 - 仅在同意被明确批准 之后 将
state值存储在服务器端(安全会话存储或 加密 Cookie 中) - 在重定向到第三方身份提供商 之前立即 设置
state跟踪 Cookie/会话(不是在同意批准之前) - 在回调端点验证
state查询参数 与回调请求的 Cookie 中或请求的基于 Cookie 的会话中存储的值完全匹配 - 拒绝任何
state参数缺失 或不匹配的回调请求 - 确保
state值是单次使用的(验证后删除)并 具有较短的过期时间(例如 10 分钟)
state 值的同意 Cookie 或会话 不得
在用户在
MCP 服务器授权端点批准同意屏幕 之前 设置。在同意批准之前设置此 Cookie 会使同意屏幕无效,因为攻击者可以通过制作恶意授权请求来绕过它。
令牌透传
“令牌透传”是一种反模式,其中 MCP 服务器接受来自 MCP 客户端的令牌,而不验证令牌是否已正确颁发 给 MCP 服务器,并将它们透传到下游 API。风险
令牌透传在 授权规范 中被明确禁止,因为它引入了许多安全风险,包括:- 安全控制规避
- MCP 服务器或下游 API 可能实施重要的安全 控制,如速率限制、请求验证或流量 监控,这些控制依赖于令牌受众或其他凭据 约束。如果客户端可以直接获取和使用令牌与下游 API 通信,而 MCP 服务器未正确验证它们或 确保令牌是为正确的服务颁发的,它们就会绕过这些控制。
- 责任和审计跟踪问题
- 当客户端使用上游颁发的访问令牌调用时,MCP 服务器将无法识别或区分 MCP 客户端,该令牌对 MCP 服务器可能是不透明的。
- 下游资源服务器的日志可能显示请求似乎来自具有不同身份的不同源,而不是实际转发令牌的 MCP 服务器。
- 这两个因素都使事件调查、控制和审计 变得更加困难。
- 如果 MCP 服务器传递令牌而不验证其声明 (例如角色、权限或受众)或其他元数据,拥有被盗令牌的恶意行为者可以使用服务器作为数据泄露的代理。
- 信任边界问题
- 下游资源服务器授予特定实体信任。 此信任可能包括关于来源或客户端行为 模式的假设。破坏此信任边界可能导致意外 问题。
- 如果令牌被多个服务接受而未进行适当 验证,攻击者破坏一个服务后可以使用令牌 访问其他连接的服务。
- 未来兼容性风险
- 即使 MCP 服务器今天开始作为“纯代理”,它以后可能也需要 添加安全控制。从适当的令牌受众 分离开始,更容易演进安全模型。
缓解措施
MCP 服务器 不得 接受任何未明确 颁发给 MCP 服务器的令牌。服务器端请求伪造 (SSRF)
服务器端请求伪造 (SSRF) 是一种攻击,攻击者可以 诱导 MCP 客户端向非预期的目的地发出 HTTP 请求, 可能访问内部网络资源、云元数据 端点或其他受保护的服务。攻击描述
在 OAuth 元数据发现期间,MCP 客户端从几个来源获取 URL,这些来源可能由恶意 MCP 服务器控制:- 来自
WWW-Authenticate头部的resource_metadataURL - 来自受保护资源元数据
文档的
authorization_serversURL - 来自授权服务器元数据的
token_endpoint、authorization_endpoint和其他 URL
- 直接内部 IP 访问:像
http://192.168.1.1/admin或http://10.0.0.1/api这样的 URL 针对内部网络服务 - 云元数据端点:针对
http://169.254.169.254/(AWS/GCP/Azure 元数据服务) 的 URL 可以 泄露云凭据和实例信息 - 本地主机服务:像
http://localhost:6379/这样的 URL 可以与 本地服务交互(Redis、数据库、管理面板) - DNS 重绑定:在验证和使用之间更改 DNS 解析的域(例如
https://attacker.com最初解析为安全 IP,然后解析为192.168.1.1) - 重定向链:看起来正常但重定向到内部 资源的 URL
风险
- 凭据泄露:云元数据端点通常暴露 IAM 凭据、API 密钥和其他秘密
- 内部网络侦察:错误消息揭示有关 内部网络拓扑和服务的信息
- 服务交互:POST 请求(例如到令牌端点)可以 触发内部服务上的变更
- 防火墙绕过:MCP 客户端充当代理,绕过网络 周边控制
- 数据泄露:内部服务响应可能通过错误消息或 OAuth 流程反射回 攻击者
缓解措施
部署到服务器的 MCP 客户端 必须 考虑 SSRF 风险,并在获取 OAuth 相关 URL 时实施适当的缓解措施。哪些保护措施是适当的取决于您的网络环境。 强制 HTTPS MCP 客户端 应该 在生产环境中要求所有 OAuth 相关 URL 使用 HTTPS:- 拒绝
http://URL,开发期间环回地址 (localhost、127.0.0.1、::1) 除外 - 这与 OAuth 2.1 第 1.5 节 一致,该节要求除环回 重定向 URI 外,所有 OAuth 协议 URL 都必须使用 HTTPS
- 为开发/测试 场景提供明确的退出机制
- 私有 IPv4 范围:
10.0.0.0/8、172.16.0.0/12、192.168.0.0/16 - 环回:
127.0.0.0/8、::1(除非明确允许用于 开发) - 链路本地:
169.254.0.0/16(包括云元数据端点) - 私有 IPv6 范围:
fc00::/7、fe80::/10
避免手动实现 IP 验证。攻击者利用编码技巧
(八进制、十六进制、IPv4 映射的 IPv6),自定义解析器通常会忽略这些技巧。
- 不要盲目跟随重定向到内部资源
- 对重定向目的地应用 HTTPS 和 IP 范围限制
- 考虑禁用自动重定向跟随并验证每一跳
- 通过阻止内部目的地的代理路由 OAuth 发现请求
- 使用像 Smokescreen 或类似 出口代理这样的工具,从设计上防止 SSRF
- 配置网络策略以限制 MCP 客户端的出站 访问
- 攻击者的域可能在验证期间解析为安全 IP,但在 实际请求期间解析为内部 IP
- 考虑在检查和使用之间固定 DNS 解析结果
- 深度防御:将 DNS 检查与其他缓解措施结合
资源和工具
以下资源可以帮助开发者在 MCP 客户端中实施 SSRF 保护。 参考文档- OWASP SSRF 预防速查表: 关于 SSRF 预防技术的综合指南,包括输入 验证、允许列表策略和网络级控制
- OWASP Top 10 A10:2021 - SSRF: 最关键 Web 应用程序安全风险背景下的 SSRF
会话劫持
会话劫持是一种攻击向量,其中服务器向客户端提供 会话 ID,未经授权的方能够获取 并使用该会话 ID 冒充原始客户端并 代表其执行未经授权的操作。会话劫持提示注入
会话劫持冒充
攻击描述
当您有多个处理 MCP 请求的有状态 HTTP 服务器时, 以下攻击向量是可能的: 会话劫持提示注入- 客户端连接到 服务器 A 并接收会话 ID。
-
攻击者获取现有会话 ID 并向 服务器 B 发送恶意
事件,使用该会话 ID。
- 当服务器支持 重新交付/可恢复流 时, 在接收响应之前故意终止请求 可能导致原始客户端通过 GET 请求恢复服务器发送事件。
- 如果特定服务器作为工具调用的后果发起服务器发送事件,例如
notifications/tools/list_changed,其中可以影响 服务器提供的工具,客户端最终可能 拥有他们不知道已启用的工具。
- 服务器 B 将事件(与会话 ID 关联)入队到 共享队列。
- 服务器 A 使用会话 ID 轮询队列中的事件并 检索恶意负载。
- 服务器 A 将恶意负载作为 异步或恢复的响应发送给客户端。
- 客户端接收并根据恶意负载操作,导致 潜在泄露。
- MCP 客户端向 MCP 服务器认证,创建 持久会话 ID。
- 攻击者获取会话 ID。
- 攻击者使用会话 ID 向 MCP 服务器发出调用。
- MCP 服务器不检查额外授权并将 攻击者视为合法用户,允许未经授权访问或 操作。
缓解措施
为了防止会话劫持和事件注入攻击,应实施以下缓解措施: 实施授权的 MCP 服务器 必须 验证所有入站 请求。MCP 服务器 不得 使用会话进行认证。 MCP 服务器 必须 使用安全、非确定性的会话 ID。 生成的会话 ID(例如 UUID)应该 使用安全随机数 生成器。避免可预测或顺序的会话标识符,这些标识符可能被攻击者猜测。轮换或过期会话 ID 也可以降低风险。 MCP 服务器 应该 将会话 ID 绑定到用户特定信息。 存储或传输会话相关数据时(例如在队列中), 将会话 ID 与授权用户独有的信息结合,例如他们的内部用户 ID。使用像<user_id>:<session_id> 这样的键格式。这确保即使攻击者猜测
会话 ID,他们也无法冒充另一个用户,因为用户 ID 是从用户令牌派生的,而不是由客户端提供的。
MCP 服务器可以选择利用其他唯一标识符。
本地 MCP 服务器泄露
本地 MCP 服务器是在用户本地机器上运行的 MCP 服务器, 要么由用户下载并执行服务器,要么自己编写 服务器,要么通过客户端的配置流程安装。 这些服务器可能直接访问用户的系统,并且可能被用户机器上运行的其他进程访问,使它们成为有吸引力的攻击目标。攻击描述
本地 MCP 服务器是在与 MCP 客户端相同的机器上下载和执行的二进制文件。如果没有适当的沙盒和同意 要求,以下攻击成为可能:- 攻击者在客户端 配置中包含恶意“启动”命令
- 攻击者在服务器本身内分发恶意负载
- 攻击者通过 DNS 重绑定访问留在 localhost 上运行的不安全本地服务器
风险
来自不受信任来源的限制不足的本地 MCP 服务器 引入了几个关键安全风险:- 任意代码执行。攻击者可以以 MCP 客户端权限执行任何命令。
- 无可见性。用户无法洞察正在 执行什么命令。
- 命令混淆。恶意行为者可以使用复杂或 曲折的命令来显得合法。
- 数据泄露。攻击者可以通过被泄露的 JavaScript 访问合法的本地 MCP 服务器。
- 数据丢失。合法服务器中的攻击者或错误可能导致主机机器上 不可恢复的数据丢失。
缓解措施
如果 MCP 客户端支持一键式本地 MCP 服务器配置,它 必须 在执行命令之前实施适当的同意机制。 预配置同意 在通过一键式配置连接新的本地 MCP 服务器之前,显示清晰的同意对话框。MCP 客户端 必须:- 显示将要执行的确切命令,未经截断 (包括参数和参量)
- 清楚地将其标识为在用户系统上执行代码的潜在危险操作
- 在继续之前需要明确的用户批准
- 允许用户取消配置
- 突出显示潜在的危险命令模式(例如包含
sudo、rm -rf、网络操作、预期目录外的文件系统访问的命令) - 对访问敏感位置(主 目录、SSH 密钥、系统目录)的命令显示警告
- 警告 MCP 服务器以与客户端相同的权限运行
- 在具有最小默认权限的沙盒环境中执行 MCP 服务器命令
- 启动对文件系统、网络 和其他系统资源具有受限访问权限的 MCP 服务器
- 在需要时为用户提供明确授予额外权限的机制(例如特定目录访问、网络访问)
- 使用平台适当的沙盒技术(容器、chroot、 应用程序沙盒等)
- 保持沙盒解决方案最新,以考虑新出现的 漏洞
- 使用
stdio传输将访问限制为仅限 MCP 客户端 - 如果使用 HTTP 传输,则限制访问,例如:
- 需要授权令牌
- 使用具有受限访问的 unix 域套接字或其他进程间通信 (IPC) 机制
范围最小化
糟糕的范围设计会增加令牌泄露影响,提升用户 摩擦,并模糊审计跟踪。攻击描述
攻击者获取(通过日志泄露、内存抓取或本地 拦截)携带广泛范围 (files:*、db:*、
admin:*) 的访问令牌,这些令牌是预先授予的,因为 MCP 服务器在 scopes_supported 中暴露了每个范围,并且客户端请求了所有范围。
该令牌启用横向数据访问、权限链式反应,并且难以
撤销而无需重新同意整个表面。
风险
- 爆炸半径扩大:被盗的广泛令牌启用不相关的 工具/资源访问
- 撤销摩擦更高:撤销最大权限令牌会破坏 所有工作流
- 审计噪音:单个总括范围掩盖了每次操作的用户意图
- 权限链式反应:攻击者可以立即调用高风险工具 而无需进一步提升提示
- 同意放弃:用户拒绝列出过多范围的对话框
- 范围膨胀盲区:缺乏指标使过宽请求 正常化
缓解措施
实施渐进式、最小权限范围模型:- 最小初始范围集(例如
mcp:tools-basic),仅包含 低风险发现/读取操作 - 当首次尝试特权操作时,通过定向
WWW-Authenticatescope="..."挑战进行增量提升 - 降范围容忍:服务器应接受减少范围的令牌; 授权服务器可以颁发请求范围的子集
- 发出精确的范围挑战;避免返回完整目录
- 记录提升事件(请求的范围、授予的子集),并带有 关联 ID
- 仅从基线范围(或初始
WWW-Authenticate指定的范围)开始 - 缓存最近的失败,以避免对被拒绝范围的重复提升循环
常见错误
- 在
scopes_supported中发布所有可能的范围 - 使用通配符或总括范围 (
*、all、full-access) - 捆绑不相关的权限以预先防止未来提示
- 在每个挑战中返回整个范围目录
- 无声的范围语义更改而不版本控制
- 将令牌中声明的范围视为充分,而没有服务器端 授权逻辑