MCP 授权与安全边界
本节目标
学完后,你应能:
- 在连接 server 前列出资产、主体、信任边界和攻击入口。
- 分清 MCP Authorization、第三方下游授权、session 与用户同意。
- 解释 token passthrough、confused deputy、SSRF、DNS rebinding、路径穿越和提示注入风险。
- 为本地 stdio、远程 Streamable HTTP、roots、sampling、elicitation 与 tools 配置相称控制。
安全从“谁代表谁做什么”开始
先不要从“是否用了 OAuth”开始。为每条连接回答:
- 主体:用户、host、client、MCP server、下游 API 分别是谁?
- 资产:对话、文件、数据库、令牌、写权限、模型调用额度有哪些?
- 边界:数据从哪里跨到哪里?哪个组件可看到明文?
- 授权:哪个凭据授权哪个受众、哪些 scope、哪个用户?
- 同意:用户看到了什么,能否拒绝,撤销后怎样生效?
- 恢复:写操作能否幂等、撤销或审计?
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 passthrough | MCP server 把面向自己的 access token 原样交给下游 API | 是;下游地址可以完全可信 | 为每个 resource 获取独立、正确 audience 的凭据,MCP token 只在 MCP resource server 验证 |
| confused deputy | 攻击者诱使一个本来有权限的 server 代表错误主体、tenant、资源或意图行权 | 是;server 可能使用自己合法的下游 token | 把 subject/tenant/resource/intent 与授权决策绑定,临执行重检,高风险动作确认与审计 |
| SSRF | 不可信输入控制 server 的出站目标或重定向链 | 是;请求甚至可以不带 token | scheme/host allowlist、解析后 IP 策略、逐跳重验、出站网络隔离、超时与大小预算 |
| prompt injection | resource/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 的“建议操作范围”,不是自动文件系统沙箱。安全实现至少需要:
- 把输入 URI 转成规范化绝对路径。
- 明确允许的 scheme;当前 root 使用
file://。 - 在解析
..、junction、symlink 后再次确认最终路径仍在允许根内。 - Windows 路径比较处理盘符、大小写和 UNC 边界。
- 读写权限分离;root 可见不等于可写。
- 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 |
| 检测/恢复 | 脱敏审计、幂等键、查询状态、删除/回滚流程 |
自测
- capability、authorization 与 consent 分别回答什么问题?
- 为什么 client 的 MCP token 不能直接传给第三方 API?
- form elicitation 为什么不能索要 API key?URL mode 又解决了什么边界?
- root 已限制到项目目录,为什么仍要处理 symlink/junction?
- session ID 为什么不能作为用户身份?
- resource 中一句恶意指令如何跨越到另一个 server 的 tool?应在哪里阻断?
能对上述场景给出预防、检测和恢复三类控制,才算掌握。
下一步
进入 Inspector、调试与版本管理,学习怎样在不泄露秘密的前提下收集证据。
参考资料
以下均为 MCP 第一方资料,获取/复核日期:2026-07-21。