MCP 授权与安全边界

本节目标

学完后,你应能:

  • 在连接 server 前列出资产、主体、信任边界和攻击入口。
  • 分清 MCP Authorization、第三方下游授权、session 与用户同意。
  • 解释 token passthrough、confused deputy、SSRF、DNS rebinding、路径穿越和提示注入风险。
  • 为本地 stdio、远程 Streamable HTTP、roots、sampling、elicitation 与 tools 配置相称控制。

安全从“谁代表谁做什么”开始

先不要从“是否用了 OAuth”开始。为每条连接回答:

  1. 主体:用户、host、client、MCP server、下游 API 分别是谁?
  2. 资产:对话、文件、数据库、令牌、写权限、模型调用额度有哪些?
  3. 边界:数据从哪里跨到哪里?哪个组件可看到明文?
  4. 授权:哪个凭据授权哪个受众、哪些 scope、哪个用户?
  5. 同意:用户看到了什么,能否拒绝,撤销后怎样生效?
  6. 恢复:写操作能否幂等、撤销或审计?

capability 只回答协议功能;authentication 证明身份;authorization 决定权限;consent 让用户理解并选择。四者不可互相替代。

一张基础威胁图

用户
  │ 同意/输入
  ▼
host + client ── MCP 凭据 ──► MCP server ── 下游凭据 ──► 第三方 API
  │ roots / model / UI             │ tools / resources
  └──────── 不可信内容与请求 ◄─────┘

重点观察两套凭据:

  • client 访问 MCP server 的凭据;
  • MCP server 访问第三方 API 的凭据。

它们服务不同受众,不能把第一套直接透传成第二套。

远程授权:不做 token passthrough

官方安全最佳实践明确禁止 token passthrough。MCP server 收到 token 后必须确认:

  • token 是发给当前 resource server 的,即 audience/resource 正确;
  • issuer、签名、有效期和 scope 符合预期;
  • 当前 subject 对目标资源有权限;
  • 不把该 token 原样转发给下游服务。

在 2025-11-25 的 HTTP 授权流程中,client 必须在 authorization request 与 token request 都发送 RFC 8707 resource,指向目标 MCP server 的 canonical URI;每个后续 HTTP request 都必须重新携带 bearer token。Server 不能只检查“token 能解码”或“scope 名字存在”,还必须验证 token 由其信任的 authorization server 签发且 audience 面向当前 MCP server。无效、过期或错误 issuer/audience 的 token 应得到 401,权限或 scope 不足得到 403,畸形授权请求得到 400。stdio 不套用这套 HTTP OAuth wire flow,而应从受控环境取得凭据。

教学验证器的控制面

离线项目 用 wire 外的 transport_context 和 authorization_revision 模拟“上游已完成 token 验证后”的身份快照。它们不是 MCP message 字段,也不能证明真实 token 的签名、issuer、expiry、introspection 或 401/403 流程正确。

进一步的 Loopback HTTP 项目 会执行真实 POST/GET/DELETE、401/403、WWW-Authenticate、Protected Resource Metadata 文档/challenge 形状与 session 往返,并验证 audience/resource/scope/tenant/revision/expiry/revocation;但其 token 仍来自明确标注的离线 policy,且 http://127.0.0.1 resource 不满足 RFC 9728 的 HTTPS 要求,依然不证明授权码、PKCE、签名/JWKS、issuer discovery、introspection 或 PRM 部署正确。

否则会产生 confused deputy:攻击者借一个被信任的 MCP server 使用不属于该下游受众的凭据。日志也无法准确说明调用代表谁。

正确的下游授权模式通常是:MCP server 自己作为第三方 OAuth client,获得并安全保存与用户身份绑定的下游 token。2025-11-25 的 URL elicitation 可把用户引导到 server 控制的 HTTPS 页面完成第三方授权;秘密不经过 client 或 LLM。它不能替代 client 到 MCP server 的 MCP Authorization。

不要把几个相邻风险混成“都是 OAuth 问题”:

风险最小判定即使没有另一风险也会发生主要控制
token passthroughMCP server 把面向自己的 access token 原样交给下游 API是;下游地址可以完全可信为每个 resource 获取独立、正确 audience 的凭据,MCP token 只在 MCP resource server 验证
confused deputy攻击者诱使一个本来有权限的 server 代表错误主体、tenant、资源或意图行权是;server 可能使用自己合法的下游 token把 subject/tenant/resource/intent 与授权决策绑定,临执行重检,高风险动作确认与审计
SSRF不可信输入控制 server 的出站目标或重定向链是;请求甚至可以不带 tokenscheme/host allowlist、解析后 IP 策略、逐跳重验、出站网络隔离、超时与大小预算
prompt injectionresource/prompt/tool 输出中的文本试图改写控制流是;不需要网络请求保持数据/指令分层,工具授权由可信 host/policy 决定,不让内容自授权限

因此,“验证了 audience”只能证明 token 面向当前 MCP resource;它不能证明下游调用代表了正确用户,也不能阻止 URL 参数访问云 metadata。反过来,SSRF allowlist 也不能把一个错误 audience 的 token 变成可用凭据。

Elicitation 的秘密边界

Form mode

适合普通结构化信息:语言、日期、显示名称、选项。server 不得用它请求:

  • 密码、API key、access token;
  • 支付凭据或可授权交易的秘密;
  • 任何不应进入 client 日志或模型上下文的凭据。

client 要显示请求 server、字段、用途,允许 review、修改、decline 和 cancel;双方都应验证受限 schema。

URL mode

适合敏感输入或第三方授权。client 应:

  • 清楚显示目标 domain/host;
  • 在导航前取得同意;
  • 只接受安全 URL 策略,防 javascript:、data:、不可信重定向和同形域;
  • 不把页面中输入的秘密复制回 MCP 消息。

server 不能只用 session ID 关联用户;远程场景应优先从 MCP Authorization 获得的身份(例如 subject)绑定状态。

本地 stdio server 也是可执行供应链

“只在本机运行”不等于安全。stdio server 通常继承当前 Windows 用户权限,可能获得:

  • host 传入的环境变量;
  • 工作目录及其可读文件;
  • 用户账户可访问的网络与凭据缓存;
  • 可执行下游命令的能力。

安装第三方 server 等同于安装代码依赖:

  • 核对来源、维护者、包名和发布渠道,警惕 typosquatting。
  • 固定并审查版本;升级先看变更并回归能力。
  • 使用绝对 command/path,限制工作目录和允许的 roots。
  • 只传必要环境变量;不要把整个 shell 环境和通用云凭据交给 server。
  • 能用低权限账户、容器或沙箱时再做隔离;不要把“容器化”本身当成授权。

Roots 与文件系统边界

root 是 client 告诉 server 的“建议操作范围”,不是自动文件系统沙箱。安全实现至少需要:

  1. 把输入 URI 转成规范化绝对路径。
  2. 明确允许的 scheme;当前 root 使用 file://。
  3. 在解析 ..、junction、symlink 后再次确认最终路径仍在允许根内。
  4. Windows 路径比较处理盘符、大小写和 UNC 边界。
  5. 读写权限分离;root 可见不等于可写。
  6. root 变化时使旧缓存、watcher 和授权决策失效。

不要让错误信息返回 root 外文件内容,也不要把完整敏感路径无必要发给模型。

Tool、resource、prompt 与 sampling 都是不可信输入面

Tool

  • tool annotation 在 server 不可信时必须视为不可信;“readOnly”“destructive=false”需由权限和代码验证。
  • 高风险写操作展示目标、影响和关键参数,并在临执行前确认。
  • server 端重新验证 schema、身份、资源所有权和业务规则。
  • 对重试使用幂等键或先查状态,避免重复创建/扣费。

Resource 与 prompt

  • 内容可能包含“忽略此前规则”等提示注入。
  • 保留来源标签,限制长度与 MIME,必要时在模型与工具授权之间隔离。
  • 不因 resource 来自“已连接 server”就允许它触发其他 server 的工具。
  • prompt template 是不可信模板,不可提升为系统级权限。

Sampling

  • server 提出的 prompt、tools 与上下文范围都要审查。
  • host 决定模型和配额;高风险请求保留 human-in-the-loop。
  • sampling.tools 让请求更具 agentic 能力,也扩大副作用面,必须逐工具限制。
  • 不把模型输出当成授权证明或可信数据。

Streamable HTTP 的网络风险

DNS rebinding 与 Origin

本地 HTTP server 若监听所有网卡且不校验 Origin,恶意网页可能借 DNS rebinding 访问它。规范要求校验 Origin;本地部署建议只绑定 127.0.0.1,远程连接还要正确认证。

Session hijacking

Mcp-Session-Id 用于关联协议会话,不是 bearer credential。server 应把会话与已认证用户绑定、使用安全随机值、设置过期和并发策略,并防止一个用户的 session 被另一个用户继续使用。

SSRF

OAuth 元数据、动态资源地址、URL elicitation 或 tool 参数中的 URL 都可能让 server 发起请求。控制包括:

  • 允许明确 scheme/host;
  • 解析 DNS 后阻止回环、链路本地、私网和云 metadata 地址;
  • 对每次重定向重新验证;
  • 设置请求大小、超时和出站网络策略;
  • 不把 server 返回的 URL 自动交给浏览器或下载器。

Tasks 与跨请求所有权

Tasks 会让状态跨越一个普通 request/response 周期。实现需要:

  • 任务绑定创建者的授权上下文,不能只靠高熵 task ID;
  • tasks/list 只在能识别 requestor 时暴露,否则可能泄露其他用户任务元数据;
  • tasks/get/result/cancel 检查所有权;
  • TTL 到期清理,限制轮询、并发和结果大小;
  • 不依赖可选 status notification,仍按 pollInterval 合理轮询;
  • 任务中的 elicitation/sampling 使用关联 metadata,且继续遵守各自同意边界。

日志与错误

日志应记录:时间、server/连接标识、request ID、方法、结果类别、耗时、授权决策、重试/取消;默认不要记录:

  • bearer token、cookie、API key;
  • 完整 prompt、resource 正文和表单敏感信息;
  • 不必要的本地绝对路径或个人信息。

可用字段级 allowlist 与不可逆摘要关联事件。调试模式也不能成为泄露秘密的例外。

安全审查清单

区域必须得到的证据
来源server 包、版本、维护者、更新渠道已核对
身份远程用户和 server 身份如何建立
令牌issuer/audience/resource/scope/expiry 如何验证,无 passthrough
同意用户看到哪些工具、参数、domain 与副作用
数据roots、resource、sampling context 的最小披露
网络HTTPS、Origin、bind address、SSRF 与重定向策略
工具server 端参数/业务权限校验,写操作幂等与恢复
任务task 所有权、TTL、轮询、结果访问
日志可关联且已脱敏,密钥不落盘

威胁建模练习

场景:“读取本地 Markdown,调用模型总结,再发布到远程工单系统。”

完成下表,不要只写“使用 OAuth”:

项目你的答案
主体和身份用户、host、文件 server、工单 server、下游 API
高价值资产本地正文、模型上下文、MCP token、下游 token、发布权限
入口文件 URI、resource 内容、tool 参数、URL、下游响应
最坏滥用root 越界、提示注入跨 server 调工具、错误项目发布、token 泄露
预防规范化路径、来源隔离、项目 allowlist、预览确认、最小 scope
检测/恢复脱敏审计、幂等键、查询状态、删除/回滚流程

自测

  1. capability、authorization 与 consent 分别回答什么问题?
  2. 为什么 client 的 MCP token 不能直接传给第三方 API?
  3. form elicitation 为什么不能索要 API key?URL mode 又解决了什么边界?
  4. root 已限制到项目目录,为什么仍要处理 symlink/junction?
  5. session ID 为什么不能作为用户身份?
  6. resource 中一句恶意指令如何跨越到另一个 server 的 tool?应在哪里阻断?

能对上述场景给出预防、检测和恢复三类控制,才算掌握。

下一步

进入 Inspector、调试与版本管理,学习怎样在不泄露秘密的前提下收集证据。

参考资料

以下均为 MCP 第一方资料,获取/复核日期:2026-07-21。