远程 MCP 接入怎么防令牌被转发:DPoP、令牌绑定与中转链路测评

Written by

in

更新日期:2026-07-15
适用范围:远程 MCP、OAuth 受保护资源和经由网关转发的 Agent 工具调用。本文讨论令牌重放与链路验证,不把 DPoP 当作单独解决所有授权问题的方案。

结论:令牌“能用”不等于令牌“只能由原客户端用”

远程 MCP 常把浏览器、Agent、网关和工具服务器串成一条链。若访问令牌被日志、代理、浏览器扩展或错误的重定向带走,持有该 bearer token 的任何一方都可能重放它。DPoP(Demonstrating Proof-of-Possession)提供了一个重要补强:客户端针对每个 HTTP 请求签名,资源服务器验证签名、方法、目标 URI 与时间相关声明,从而把令牌的使用与持有私钥的客户端绑定。RFC 9449 定义了 DPoP;RFC 9700 则总结了 OAuth 2.0 的安全最佳实践。

这并不意味着“加了 DPoP 就安全”。它只能降低已泄露令牌被直接重放的概率,前提是资源服务器正确验证证明、私钥未泄露、代理没有改变目标语义,且授权范围仍遵循最小权限。

先画出远程 MCP 的授权边界

一个可审计的最小链路应明确以下角色:

角色 应持有什么 不应持有什么
浏览器/Agent 客户端 短期私钥、当前会话状态 长期服务端密钥、全租户 token
授权服务器 客户端登记和授权策略 工具业务数据
网关 路由与策略决策、可脱敏审计 可导出的明文令牌日志
MCP 工具服务器 经验证的访问令牌及细粒度 scope 不受限的上游管理权限

每次转发前都要问:目标服务是否仍然是原 token 的受众?scope 是否仍对应当前工具?调用是否超出用户发起的会话?这些问题无法由单纯的 HTTP 200 回答。

DPoP 验证清单

资源服务器或网关收到 DPoP 证明后,至少验证:

  1. JWS 签名与内嵌公钥可验证,算法符合本系统允许列表;
  2. htm 与实际 HTTP 方法一致;
  3. htu 与规范化后的目标 URI 一致,不能只比较 host;
  4. iat 在允许时钟偏差内,jti 在短窗口内不可重复;
  5. access token 的 cnf.jkt 与证明中的公钥指纹匹配;
  6. 若使用 nonce,客户端能在收到挑战后重新取证,而不是把旧证明重发;
  7. 反向代理改写路径或 scheme 后,验证点仍使用对外可见的原始 URI。

其中第 3 和第 7 点最容易在中转链路里被忽略。网关若将 /mcp/files/read 改写为内部 /v1/tool/read,但资源服务器拿内部路径校验 htu,合法请求会失败;若只比较域名,则攻击者可能把证明复用到同主机的另一路径。

可复现实验设计

测试环境应包含一个授权服务器、一个反向代理、一个受保护 MCP 工具和两个客户端密钥。为每一步保存脱敏日志:request_idjti 哈希、jkt、方法、外部 URI、验证结果和拒绝原因。至少执行以下负例:

A. 用同一 DPoP 证明重放同一请求 -> 应被 jti 去重拒绝
B. 将 GET 证明改用于 POST -> htm 不匹配
C. 将 /read 证明改用于 /write -> htu 不匹配
D. 更换客户端密钥但沿用 access token -> cnf.jkt 不匹配
E. 令 iat 过期或未来时间过大 -> 时间窗口拒绝

这些用例验证的是控制逻辑,不代表某个供应商默认已经开启 DPoP。只有把实际返回码、代理配置版本和验证日志一起保存,才构成可复盘的安全证据。

与最小权限、回滚一起使用

DPoP 不替代短生命周期 token、细粒度 scope、租户隔离、工具审批和密钥轮换。高影响工具(删除数据、部署、付款、外发消息)仍应有单独确认或策略门。发生异常时,先吊销对应会话或 client key,再检索同一 jkt 关联的调用;不要依赖“令牌已经过期”作为唯一处置手段。

对于需要快速接入多模型和工具路由的团队,top-api.cc 这类中转层应把 token 处理与模型 API 密钥隔离,并提供按租户、路由和工具范围的调用审计。上线前先把上述负例跑通,再允许真实用户授权。

限制

DPoP 的互操作性取决于授权服务器、客户端和资源服务器同时支持相关流程;遗留 SDK、WebSocket 或代理重写环境需要额外设计。本文不构成 OAuth 部署指南,也不能替代对具体 MCP 实现、浏览器存储方式和组织授权策略的安全评估。

参考资料

  1. RFC 9449:OAuth 2.0 Demonstrating Proof of Possession
  2. RFC 9700:OAuth 2.0 Security Best Current Practice

私钥、nonce 与审计的运行边界

私钥应只存在于能够代表当前客户端的受保护存储中,不应随着 access token 一起写入日志、配置中心或工具参数。服务端验证 jti 时,保存短期哈希即可;保存完整 DPoP JWT 会扩大敏感数据留存。多实例网关要共享或一致地访问短期去重存储,否则同一证明可能从不同实例穿透。

nonce 机制适合降低时钟偏差和重放窗口带来的不确定性,但客户端必须把挑战当作一次新的签名请求,而不是原封不动重传旧 header。审计系统则应记录验证结论而非原 token:例如 dpop_valid=truereason=htu_mismatchkey_thumbprint_hash。这样既能排查路由问题,也不会把可利用凭据放进日志。

最后,把 token 交换、资源访问和工具执行三类失败分别计数。授权服务器签发失败、网关证明校验失败、工具 scope 拒绝需要不同的告警与处置人;混为“MCP 调用失败”会让真正的授权异常被业务错误淹没。

发布前确认

上线前让客户端、网关和工具服务各自输出一次脱敏验证日志,确认它们对同一请求的
equest_id、目标 URI 与授权范围理解一致。任何一端无法解释拒绝原因时,都应停在灰度环境继续排查。

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *