先画清四个身份和信任域
下游业务 API 可能构成第五个信任域。MCP Server 若需访问它,应使用 On-Behalf-Of、Token Exchange 或独立服务身份;直接转发收到的 MCP Token 会破坏 Audience 边界,形成 Confused Deputy。
模型位于 Host 的决策链中,但不是身份主体。Prompt 中出现“我已获得管理员批准”不能改变服务端授权事实,模型输出也不应包含 Token。
本地 stdio 与远程 HTTP 风险不同
本地进程
stdio Server 继承的是操作系统进程权限,不经过 OAuth 就可能读取文件、环境变量、SSH Agent 或本地凭据。安装来源、启动命令、工作目录、环境白名单、沙箱和自动更新链因此比“是否走 HTTPS”更重要。不要把社区配置中的一条命令当成无害插件。
远程服务
远程 Server 面对 OAuth、TLS、租户隔离、地域、服务供应链和元数据发现风险。公开可达并不代表可信;组织应维护 Server 注册、Owner、数据类别、允许 Scope、审计保留和下线流程。
| 形态 | 主要失陷路径 | 默认控制 |
|---|---|---|
| stdio | 恶意包、命令替换、继承宿主权限 | 来源锁定、最小环境、沙箱、安装差异审查 |
| 远程 HTTP | Token 混淆、租户越权、元数据/重定向攻击 | TLS、OAuth 资源绑定、Issuer/Audience 校验、租户授权 |
远程授权的关键语义
HTTP MCP Server 是 OAuth Protected Resource。未授权请求返回 401,并通过 RFC 9728 Protected Resource Metadata 告诉 Client 可用 Authorization Server;Client 再执行 Authorization Code + PKCE 等适合其类型的安全流程。2026-07-28 授权规范进一步强化 Issuer 绑定与发现。
Authorization Server 返回的 iss 要与预期 Issuer 一致,Client Credential 也绑定到签发它的 Issuer。动态客户端注册已在当前 MCP 规范中进入弃用路径,新的企业实现应评估 Client ID Metadata Documents,而不是继续扩大 DCR 依赖。
Scope 从最小授权开始并支持按需升级
首次连接只申请当前任务需要的最小 Scope。真正调用高权限 Tool 时,Server 可通过挑战返回额外 Scope,Host 显示增量权限和原因后执行 Step-up。用户拒绝扩权时,原有低权限连接应继续可用,而不是把整个会话变成不可恢复错误。
Scope 是粗粒度能力上限,不适合承载每个订单、文档或租户的资源权限。Server 必须结合 Token 主体、租户、资源 ACL 和 Tool 参数做最终授权。审计记录至少关联用户、Client、Resource、Scope、Tool、参数摘要、策略版本和授权结果。
禁止 Confused Deputy 与 Token Passthrough
MCP Server 必须验证签名、Issuer、Audience/Resource、过期、Not-Before 和所需 Scope。只验“Token 有效”却不验 Audience,会让签给服务 A 的 Token 被拿去访问服务 B。Client 也不能把某个 Server 的 Token 复用到另一个 Server。
Server 调下游 API 时,收到的 Token 未必适合下游 Audience。安全做法是受控交换为下游 Token,或用服务身份并显式传递授权上下文。任何 Token Passthrough 都要视作协议设计缺陷,而不是便利优化。
// 简化的服务端授权结果;真实实现应由受审计中间件完成
type AuthorizationDecision = {
subject: string
tenant: string
resource: string
audienceMatched: boolean
grantedScopes: string[]
policyVersion: string
}
function assertAuthorized(d: AuthorizationDecision, scope: string) {
if (!d.audienceMatched || !d.grantedScopes.includes(scope)) {
throw new Error('FORBIDDEN')
}
}
协议授权不等于每次业务动作获批
连接授权说明某个 Client 在一段时间内“可以请求”哪些能力;动作审批判断“现在是否用这些参数执行”。发送邮件、支付、删除、权限授予等副作用必须基于参数、可逆性和风险重新评估。Tool 描述里的 readOnlyHint 不能作为强制安全依据。
高风险动作应先生成可审阅计划或 Diff,用户确认后由 Host 颁发短时、绑定 Tool 与参数摘要的一次性批准。Server 再验证批准,而不是让模型在 Prompt 里自报“用户已同意”。
Server 端的最小安全基线
- 输入:JSON Schema 只是第一层;继续做路径、URL、SQL、资源归属和业务不变量校验。
- 输出:过滤敏感字段,控制大小与 MIME,把 Prompt/Resource/Tool 结果标为外部数据。
- 运行:租户隔离、速率限制、Deadline、幂等、审计与可撤销执行。
- 供应链:锁定依赖、镜像、构建来源和发布签名;升级时审查新增 Tool 与权限。
- 秘密:不注入不需要的环境变量,不写日志,不经模型 Context 传递。
这些控制必须在 Server 或其下游业务系统生效。只在 Host UI 隐藏 Tool,不足以阻止被盗 Client、直接 HTTP 调用或错误配置。
测试协议正常之外的攻击路径
| 攻击/故障 | 预期结果 | 证据 |
|---|---|---|
| 伪造 Resource Metadata 或 Issuer | 发现链被拒绝,不发授权码 | Client 安全日志 |
| 错 Audience 或过宽 Scope | Server 401/403 且不触发业务动作 | 授权决策 Trace |
| Token Passthrough 到下游 | 下游拒绝或交换为正确 Audience | Token 交换/拒绝记录 |
| Resource 中的注入指令 | 作为数据呈现,不提升指令权威 | Host Context 快照与 Eval |
| 撤销后的在途 Run | 下一授权点失败,缓存同步失效 | 撤销演练与终态 |
| 跨租户参数替换 | 服务端按主体和资源拒绝 | 负向契约测试 |
安全评审的最终产物不是“采用 OAuth”一句话,而是一份威胁模型、Scope/动作矩阵、凭据数据流、负向测试、撤销 Runbook 和残余风险 Owner。
主动提取:为什么 Token 有合法签名仍可能越权?
约束变化:Server 从本地 stdio 改成远程 HTTP,原安全评审为什么不能沿用?
资料与事实边界
以下以官方文档、标准、论文和原始工程资料为主。厂商 API、价格、限额与模型能力会变化,落地时仍需按目标版本做能力探测和回归。
- MCP Authorization 2026-07-28 — HTTP 传输授权、资源发现、Scope 与安全要求
- MCP Security Best Practices — Confused Deputy、Token Passthrough 与本地 Server 风险
- RFC 9728 OAuth 2.0 Protected Resource Metadata — Protected Resource 与授权服务发现
- RFC 8707 Resource Indicators for OAuth 2.0 — 把 Access Token 绑定目标资源
- RFC 9207 OAuth Authorization Server Issuer Identification — 授权响应 Issuer 识别与 Mix-Up 防护