保护 MCP 服务器:OAuth、受众绑定与混淆代理
- 理解为什么一个远程(HTTP)MCP 服务器是一个 OAuth 2.1 资源服务器,而不仅仅是一个 API 密钥端点
- 追踪发现握手过程:401 → Protected Resource Metadata → Authorization Server Metadata → 令牌
- 解释令牌受众绑定(RFC 8707)以及它为什么能阻止一个服务的令牌在另一个服务上生效
- 指出混淆代理陷阱以及封堵它的那一条规则:绝不将客户端的令牌透传给上游 API
- 在把 MCP 服务器暴露到互联网之前,应用一份简短的加固检查清单
MCP 已经从新鲜事物变成了智能体触达工具的默认方式——这意味着 MCP 服务器如今正守在真实数据和真实操作的前面。你通过 STDIO 启动的本地服务器信任它所处的环境:它从环境变量读取凭据,并且没有需要防御的网络边界。而一旦你把同一个服务器变成远程(HTTP),任何能访问到该 URL 的人都可以尝试调用它。这就把它变成了一个授权问题,而 MCP 规范用 OAuth 2.1 来回应——而不是一套定制的 API 密钥方案。
本页讨论的是远程场景。如果你的服务器只用 STDIO,规范明确表示不要走 OAuth 流程——从环境中获取凭据然后继续即可。
三种角色
OAuth 把问题拆分给三方。MCP 干净地映射到它们之上:
关键的思维转变:MCP 服务器自己从不处理登录。 它只验证别人签发的令牌。正是这种分离让你能在一个自己编写的服务器前面放上一个现成的身份提供方。
发现握手
客户端不应当需要预先配置好在哪里进行认证。MCP 让发现过程自动化,由一个 401 驱动:
- 第一个请求裸奔发出。服务器以 HTTP 401 Unauthorized 拒绝它,并附带一个指向其资源元数据 URL 的 WWW-Authenticate 头。
- 它对服务器的 /.well-known/oauth-protected-resource 发起 GET 请求。该文档的 authorization_servers 字段列出了至少一个客户端可用的 Authorization Server。
- 它对 AS 的 /.well-known/oauth-authorization-server 发起 GET 请求,以了解 authorize 和 token 端点以及所支持的能力。
- 如果客户端在这个 AS 上没有 client ID,它可以 POST /register 在无人参与的情况下获取一个——这一点至关重要,因为客户端无法预先知道每一个 MCP 服务器。
- 客户端生成 PKCE verifier/challenge,打开浏览器访问包含 resource 参数的 authorize URL,用户同意,然后客户端用返回的 code(连同 verifier)换取访问令牌。
- 现在每个请求都携带 Authorization: Bearer <token>。服务器验证它并作出响应。
注意客户端一侧没有任何硬编码的认证配置——是那个 401 引导了一切。这正是重点所在:智能体可以连接到一个它从未见过的服务器,并弄清楚该如何认证。
受众绑定:那条承重的规则
下面就是受众绑定要防范的失效模式。假设某个用户持有一个为 calendar.example.com 签发的令牌。位于 evil.example.com 的一个恶意(或只是马虎)的 MCP 服务器诱使客户端把那个令牌发给它。如果 evil 接受了它,它现在就可以转身以该用户的身份去调用日历 API。一个服务的令牌在另一个服务上生效了。OAuth 的安全边界就此崩塌。
修复办法是 Resource Indicators(RFC 8707):
- 在授权请求和令牌请求中,客户端都必须包含一个 resource 参数,设置为它打算调用的 MCP 服务器的规范 URI——例如 resource=https://mcp.example.com。即便不确定 AS 是否支持,它也会发送这个参数。
- 在受支持的情况下,AS 会给令牌打上标记,使其只对那个特定的资源服务器有效。
- 在做任何工作之前,MCP 服务器必须核实令牌是为它自己签发的——检查 audience 声明(RFC 9068)。为其他任何人铸造的令牌都会得到 401,没有任何余地。
授权请求上的 resource 参数(URL 编码)
&resource=https%3A%2F%2Fmcp.example.com
规范 URI 有严格要求:https://mcp.example.com 和 https://mcp.example.com:8443/mcp 是有效的;mcp.example.com(无 scheme)和 https://mcp.example.com#frag(带 fragment)则无效。为了互操作性,优先使用不带末尾斜杠的形式。
混淆代理:绝不透传令牌
这是把一个善意的 MCP 服务器变成攻击者代理的错误。它就是智能体安全中那个同样的混淆代理问题,被磨成了一条具体的规则。
MCP 服务器常常需要调用上游 API(GitHub、某个数据库服务、另一个 SaaS)。诱惑在于:把客户端交给你的令牌拿去转发到上游。不要这么做。 规范说得很直白:MCP 服务器绝不能透传它从客户端收到的令牌。
为什么危险:客户端的令牌是以你的服务器为受众签发的。如果你转发它,上游 API 可能会像它来自你一样信任它,或者假设你已经验证过它——于是一个仅限一跳的令牌就在两跳之外做起了工作,游离于任何人的同意模型之外。
- 如果你的 MCP 服务器要调用上游 API,它就是作为一个独立的 OAuth 客户端去访问那个 API,并从上游的授权服务器获取它自己的令牌。两个独立的令牌,两个独立的受众。客户端的令牌止步于你的门口。
一份起飞前的加固检查清单
在远程 MCP 服务器接触公共互联网之前:
- 所有 AS 端点都必须使用 HTTPS。重定向 URI 必须是 HTTPS 或 localhost——别无其他。
- 拒绝任何不是专门为本服务器签发的令牌。这是阻止跨服务令牌复用的那一道单一检查。
- 客户端必须使用 PKCE,这样即使授权码被拦截,没有匹配的 verifier 也毫无用处。
- AS 必须将重定向 URI 与预先注册的值精确匹配,且客户端应当使用并验证 state 参数——两者都能抵御开放重定向钓鱼。
- 签发短寿命的访问令牌以限制泄露造成的损害;对于公开客户端,轮换刷新令牌。安全地存储令牌,绝不记录它们。
- 令牌放在 Authorization 头里,绝不放在查询字符串中,否则它们会落到日志和 referrer 里。
- 受众绑定是传输层的门禁;仍要应用来自 /docs/security/securing-agents 的最小权限、沙箱化和人机协同。认证说明的是「谁」——它并不说明请求是安全的。
自我检查
自我检查
0/4来源与延伸阅读
- MCP Authorization specification (2025-06-18) — 本页所总结的规范性流程、角色以及 MUST/SHOULD 要求。
- MCP Security Best Practices — 令牌透传、混淆代理,以及为什么它们被禁止。
- RFC 8707 — Resource Indicators for OAuth 2.0 —
resource参数与受众绑定。 - RFC 9728 — OAuth 2.0 Protected Resource Metadata — 资源服务器如何公告它的授权服务器。
- RFC 8414 — OAuth 2.0 Authorization Server Metadata 与 RFC 7591 — Dynamic Client Registration。
- OAuth 2.1 draft — PKCE、通信安全以及令牌处理要求。
- AILmanac 相关内容:保护智能体与工具 · 提示注入 · Claude Code 中的 MCP。