跳到主要内容

06 · 鉴权与安全

前面都在讲“怎么通信”。这一章讲“凭什么让你通信”——HTTP 传输上的鉴权,以及散落在全规范、但极其重要的安全红线。鉴权是可选的,但一旦上 HTTP 就应当遵守这套基于 OAuth 2.1 的模型。

6.1 鉴权的角色模型

MCP 在传输层提供鉴权,让客户端代表资源所有者去访问受保护的服务器(docs/specification/draft/basic/authorization/index.mdx:12)。三个 OAuth 角色:

MCP 角色OAuth 角色职责
MCP 服务器资源服务器(RS)校验访问令牌、提供受保护资源
MCP 客户端OAuth 客户端代表用户去拿令牌、带令牌访问
授权服务器(AS)授权服务器和用户交互、签发令牌(可独立于 RS)

传输相关的取舍很清楚(docs/specification/draft/basic/authorization/index.mdx:21):HTTP 传输遵守本规范;stdio 传输不应用它——本地子进程直接从环境变量取凭证就好。

6.2 鉴权流程(PKCE 授权码流)

怎么读:从“无令牌被拒”开始,客户端一步步发现授权服务器、注册、走带 PKCE 的授权码流、最后拿到绑定了受众的令牌。

Client MCP Server(RS) Auth Server(AS)
│ 无令牌请求 │ │
│◄── 401 + WWW-Authenticate(含 resource_metadata URL)│
│ 取 Protected Resource Metadata(RFC 9728) │
│◄── 元数据(指向 AS) │ │
│ 取 AS 元数据 ─────────────────────────────────────►│
│◄── AS 元数据(授权/令牌端点) │
│ 生成 PKCE,带 resource 参数,开浏览器授权 ─────────►│
│◄── 重定向回调(授权码 + iss) │
│ 校验 iss,用 code_verifier + resource 换令牌 ──────►│
│◄── 访问令牌(+ 刷新令牌) │
│ 带 Bearer 令牌请求 ──► RS ── 校验受众 ── 返回资源 │

几个必须的基石(docs/specification/draft/basic/authorization/index.mdx:60):

  • 授权服务器必须实现 OAuth 2.1。
  • MCP 服务器必须实现 OAuth 2.0 Protected Resource Metadata(RFC 9728)——客户端就是靠它发现“该找哪个授权服务器”。
  • 授权服务器必须至少提供 RFC8414 或 OpenID Connect Discovery 之一的发现机制。

6.3 受众绑定:防“令牌被拿去别处用”

这是 MCP 鉴权最核心的安全机制。客户端必须实现 RFC 8707resource 参数——在授权请求令牌请求里都明确写出“这个令牌要给哪个 MCP 服务器用”,用服务器的规范 URIdocs/specification/draft/basic/authorization/index.mdx:218)。

对应地,服务器必须校验令牌的受众确实是自己,并且接受为自己签发的令牌,不得接受或转发任何别的令牌(docs/specification/draft/basic/authorization/index.mdx:286)。

这两条合起来防的是:令牌被某个服务器拿去冒充用户访问另一个服务器。

6.4 禁止令牌透传(token passthrough)

一条反复强调的红线:令牌透传——MCP 服务器接受客户端给自己的令牌、却不校验这令牌是否确为自己签发,就原样转发给下游第三方 API——是明令禁止的反模式(docs/docs/tutorials/security/security_best_practices.mdx:278 「### Token Passthrough」、:287 「explicitly forbidden」)。

这与 6.3 的受众绑定是一体两面:服务器只接受受众是自己的令牌(拒收别处的),自然也就不会把客户端的令牌捅到上游去。

如果服务器需要访问第三方(比如代用户调 GitHub),正确做法是走 URL 模式征询(04 章)让用户单独授权第三方:第三方令牌由服务器自己保管、绑到用户身份。规范在这里有两条对称要求——第三方凭证不得流经 MCP 客户端(客户端永远看不到第三方凭证,docs/specification/draft/client/elicitation.mdx:523),且服务器不得拿客户端给自己的凭证去访问第三方(即上面的令牌透传,elicitation.mdx:524)。这维持了“MCP 鉴权”与“第三方鉴权”两条独立的信任边界。

6.5 客户端注册与 step-up 授权

客户端拿 client ID 有三条路,draft 调整了优先级(docs/specification/draft/basic/authorization/index.mdx:90、changelog docs/specification/draft/changelog.mdx:93):

  • Client ID Metadata Documents(推荐)— 客户端直接用一个 HTTPS URL 当 client_id,授权服务器去那个 URL 抓元数据。
  • 预注册 — 用已有的 client_id
  • 动态客户端注册(RFC 7591) — draft 把它降级为已废弃,仅为兼容不支持上面那种的授权服务器而保留。

运行时遇到“权限不够”(insufficient_scope)时走 step-up 授权流:服务器回 403 + WWW-Authenticate(带所需 scope),客户端把“历史已请求的 scope ∪ 本次挑战的 scope”取并集去重新授权,避免丢掉别处需要的权限(docs/specification/draft/basic/authorization/index.mdx:401)。scope 的累积是客户端责任——这样服务器对“客户端有哪些 scope”也能保持无状态。

6.6 贯穿全协议的安全主线

把散落各章的安全要求收成一张表——这是读者最该带走的“防线清单”:

防线要求出处
人在回路工具调用、采样始终有人能否决docs/specification/draft/server/tools.mdx:30sampling.mdx:35
服务器隔离服务器看不到整段对话、看不见别的服务器docs/specification/draft/architecture/index.mdx:101
受众绑定令牌只能用于签发它的那个服务器authorization/index.mdx:286
禁止令牌透传不得拿(受众非自己的)客户端令牌转发给第三方security_best_practices.mdx:278:287
第三方凭证不经客户端第三方凭证不得流经 MCP 客户端elicitation.mdx:523
敏感信息走 URL 模式密码/密钥不得用 form 模式征询elicitation.mdx:30
requestState 当攻击者输入影响授权就必须 HMAC/AEAD + 防重放mrtr.mdx:222
校验头体一致HTTP 头与请求体不符则拒streamable-http.mdx:587
防 DNS rebinding校验 Origin、本地只绑 localhoststreamable-http.mdx:56
图标/资源当不可信icon 字节、$ref 不自动解引网络 URIbasic/index.mdx:433:299
路径遍历防护file:// 资源必须净化路径resources.mdx:433

一条值得单独点的细节:JSON Schema 的 $ref 不得被自动解引到网络 URI(防 SSRF),可选的取数模式也必须默认关闭并设白名单/超时/大小限制;复杂的组合关键字(anyOf/oneOf…)设深度与子模式上限,防恶意 schema 当 DoS 向量(docs/specification/draft/basic/index.mdx:297:310)。

6.7 边界与局限(诚实地说)

  • 鉴权只覆盖 HTTP:stdio 明确不走这套,靠环境凭证——本地信任模型不同。
  • Roots 不是访问控制:它只是“信息性指引”,协议不强制服务器待在根内(docs/specification/draft/client/roots.mdx:23)。真正的访问控制得服务器自己做。
  • 本地错误无标准码:SDK 内部的超时之类纯本地错误,规范暂未分配错误码,只要求别和“对端发来的错误”混淆(docs/specification/draft/basic/index.mdx:144)。
  • draft 是草案:协议版本 2026-07-28 仍在演进;URL 模式征询等较新特性规范明说“未来可能变”(docs/specification/draft/client/elicitation.mdx:324)。生产实现多数还在跑已发布的 2025-11-25

横向对比(同 shelf 视角)

MCP 在 ai-protocol-reference(协议库)里是**“应用↔工具”的接入协议**:它不规定模型怎么推理,只规定能力怎么被发现、调用、鉴权。

  • A2A / agent 间协议相比:MCP 管的是“一个 agent 怎么用外部工具”,不是“多个 agent 怎么互相对话”。
  • 和底层 LLM 厂商 API(Anthropic/OpenAI 的 tool use)相比:MCP 是更上一层的标准化与可组合层——同一个 MCP 服务器能被任何支持 MCP 的应用复用,而厂商 API 的工具定义绑死在单次调用里。draft 废弃 Sampling、建议“直接对接厂商 API”,正是在划清这条界:模型推理交给厂商 API,能力接入交给 MCP。

代码地图

主题文件路径符号 / 锚点
鉴权总流程与受众绑定docs/specification/draft/basic/authorization/index.mdxAuthorization Flow / Resource Parameter
客户端注册docs/specification/draft/basic/authorization/client-registration.mdxClient ID Metadata Documents
授权服务器发现docs/specification/draft/basic/authorization/authorization-server-discovery.mdx
鉴权安全要求docs/specification/draft/basic/authorization/security-considerations.mdx
令牌透传禁令docs/docs/tutorials/security/security_best_practices.mdxToken Passthrough
全局安全最佳实践docs/docs/tutorials/security/security_best_practices.mdx