基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
AIHT
内容
精选全部 AI 动态热点榜AI 日报收藏
分类
模型行业编程产品技术观点开源
更多
Agent 接入
返回首页开发者导航
AIHT
精选 / 资讯详情
8.0
热点
AI SCORE
技术实践2026-10-30 05:25

MCP 代理需分别测试令牌与业务授权

dev.to · AI#MCP#OAuth#安全测试
Editor brief · 编辑速览

文章用虚构财务连接器的策略测试样例,区分令牌是否面向当前代理与调用者是否有权执行具体业务操作。样例不涉及真实 OAuth 或 JWT 验签,强调分别建模代理接收请求和调用 ERP 时的角色。

文章思维导图
Knowledge map
拖拽缩放
AI deep read

AI 深度解读

MCP 代理验过了 Token,也可能把不该给的数据交出去:一种情况是 Token 真实有效,却根本不是签发给这个代理的;另一种情况是 Token 的接收方没错,但用户无权读取目标发票。作者把这两类缺陷拆开,用一份虚构财务连接器的 Python 策略样例,说明该怎么分别测试。

这不是一套已经验证的 OAuth 实现。它的价值在于,先把「谁能访问哪个资源、执行什么操作」写成可执行规则,再接入认证中间件和真实 ERP,避免把模型演示成功当成权限系统可靠。

两张凭证,为什么要分开管理

这个连接器有两个身份:面对 MCP client,它是接收请求的资源服务器;调用 ERP 时,它又成了客户端。即使两个服务归同一个团队维护,也不能混用这两段链路的授权。

客户端凭证止于 MCP 边界,连接器使用单独管理的凭证访问 ERP

作者引用的 MCP 2026-07-28 授权规范,要求 HTTP OAuth 流程标识 MCP 资源,并由服务器验证 Token 的预期受众(audience),这里讨论的不是 stdio 流程。原文还提醒,其引用的 OAuth 2.1 文档仍标为 IETF draft,因此应固定 MCP 修订版和 SDK 版本,不能只笼统声称「兼容 OAuth 2.1」。文章没有给出具体 SDK 版本。

资源指示符(Resource Indicators)解决的是凭证发给谁。它不负责判断某张发票能不能读。用户拥有 invoices.read,仍然可能无权访问另一个业务单元的数据。

进入 MCP 的凭证应在 MCP 资源边界终止。连接器访问 ERP,要通过部署环境认可的机制取得另一份适用凭证。文章没有替团队选择该机制,也没有在示例中接收任何原始凭证。

把权限写成规则,别让模型填写身份

示例读取发票 INV-42,所属业务单元为 north。发票归属来自可信记录查询,认证上下文来自中间件,客户端同意记录来自应用自己的存储。三者都不能听模型参数的一面之词。

下面的策略把资源、客户端同意、读取权限和业务单元四项检查集中起来:

from dataclasses import dataclass

MCP_RESOURCE = "https://finance.example/mcp"

@dataclass(frozen=True)
class VerifiedContext:
    user_id: str
    client_id: str
    resource: str
    business_unit: str
    scopes: frozenset[str]

@dataclass(frozen=True)
class Invoice:
    invoice_id: str
    business_unit: str

def allow_read(ctx, invoice, consented_clients):
    if ctx.resource != MCP_RESOURCE:
        return False, "wrong_resource"
    if (ctx.user_id, ctx.client_id) not in consented_clients:
        return False, "client_consent_missing"
    if "invoices.read" not in ctx.scopes:
        return False, "scope_missing"
    if ctx.business_unit != invoice.business_unit:
        return False, "business_unit_mismatch"
    return True, "allowed"

要盯住 ctx.resource != MCP_RESOURCE:这是应用内部必须保持的约束,不能代替密码学验证。生产中间件应在进入此函数前,完成签发者、完整性、有效期和目标资源验证,并拒绝接收方错误的 Token。

VerifiedContext 这个名字也不提供安全保证。如果路由允许调用方直接填入该结构,后面的判断就失去了可信输入。

同意记录同样做了简化。这里的 (user_id, client_id) 只能表示用户认可过某个客户端,不能证明当前申请的权限、有效授权和客户端注册状态都正确,更不能证明用户看到了正确的同意页面。

拒绝是否有效,要看数据和下游有没有出去

作者建议先绕开 LLM,直接调用工具实际使用的策略层。模型在演示中礼貌拒绝,不代表底层接口没有暴露。

下面的断言用合成记录覆盖正常读取和四种拒绝情况:

from dataclasses import replace

base = VerifiedContext(
    "lina", "purchasing-client", MCP_RESOURCE, "north",
    frozenset({"invoices.read"}),
)
invoice = Invoice("INV-42", "north")
consents = {("lina", "purchasing-client")}

assert allow_read(base, invoice, consents) == (True, "allowed")
assert allow_read(
    replace(base, resource="https://erp.example/api"),
    invoice, consents,
) == (False, "wrong_resource")
assert allow_read(
    replace(base, client_id="new-client"), invoice, consents,
) == (False, "client_consent_missing")
assert allow_read(
    replace(base, scopes=frozenset()), invoice, consents,
) == (False, "scope_missing")
assert allow_read(
    base, Invoice("INV-99", "south"), consents,
) == (False, "business_unit_mismatch")

重点看 replace(base, resource="https://erp.example/api"):它检查误接其他资源上下文时,策略能否挡住请求。但它没有提交真实 ERP Token,因此不能证明认证中间件真的会拒绝该凭证。

虚构验收场景覆盖允许读取及资源、业务单元、客户端和操作权限问题

图里还出现了未经批准的付款操作,但上面的代码只实现读取策略,没有实现付款授权。

集成测试需要继续向前走:把正确签发给 ERP 的 Token 发到 MCP 资源,确认请求被拒绝,而且没有下游调用;再用有效 MCP 凭证请求 south 的发票,确认受保护数据没有返回。

拒绝日志不是验收终点,响应和下游效果必须一起检查。

客户端同意还要单独测。作者引用的安全指引描述了一类「混淆代理」风险:代理复用固定上游身份,记住旧的同意关系,却接入了动态注册的新客户端,最终可能借旧关系放行新请求。测试应先建立已同意客户端,再引入另一个客户端,确认旧记录不能满足新请求,并覆盖真实 OAuth 库的回调验证、redirect 和 state 保护。原文同时指出,Token 透传是另一项明确禁止的行为。

模型传来的「用户之前已经同意」不能作为证据。证据必须来自服务器控制、绑定实际用户和请求客户端的记录。

我的判断

我认同作者把认证、业务授权和下游权限拆开测试。相比只靠提示词约束 Agent,这种做法能把限制落实到实际操作入口;相比只检查 scope,它又补上了目标对象和业务单元的约束。

但这份代码是策略样例,不是完整安全方案。文章没有真实部署结果,也没有攻击拦截率或方案对照数据,不能据此判断某个 MCP 代理已经安全。业务单元相等也只是虚构财务场景的规则,换到存在跨部门共享、委托访问的团队,需要重新定义对象权限。

还有两处不能遗漏。Protected Resource Metadata 负责提供发现信息,但客户端仍需验证发现目标,不能由任意工具描述决定凭证发送地址。ERP 凭证如果代表用户,要确认下游保留了哪些限制;如果代表服务,尤其是连接器共用的管理员身份,就要明确用户与业务单元限制由谁执行。文章没有提供这部分实现。

approve_invoice 也不能复用 allow_read。审批需要自己的权限、目标约束和必要审批条件,「已经连接财务系统」不等于获得全部业务操作权。代价是维护更多状态和拒绝用例,收益是每次拒绝都能对应到明确边界。

能拿来做什么

  • 如果你在 Node 服务中接入 MCP 工具,可以按示例拆出独立策略层,让工具入口和接口调用都经过同一套判断,并由认证中间件构造可信上下文。
  • 为财务读取工具先补四类拒绝测试:错误资源、未获同意的新客户端、缺少 invoices.read、跨业务单元读取;再用真实认证组件验证凭证处理。
  • 验收代理时,同时断言响应内容和 ERP 调用情况,把请求、策略结果与实际效果关联记录,确认拒绝后没有数据泄露或业务副作用。
  • 新增 approve_invoice 等写操作前,单独定义权限和审批要求;接入动态客户端注册时,再覆盖旧同意关系不能被新客户端继承的测试。
Original source

本文由 AI 通读原文后重新编写,观点部分为站内判断,细节以原文为准,来源 dev.to · AI,原文版权归原作者所有。

阅读原文
上一篇
用稳定接口构建游戏工单分类器
下一篇
Agent 身份安全要验证授权绑定关系

资讯速览

dev.to · AI · 2026-10-30 05:25

AI 评分 8.0 / 10

相关标签

#MCP#OAuth#安全测试

继续阅读

  • 用原生 JS 与 PHP 搭建站内知识助手
  • 用稳定接口构建游戏工单分类器
  • Agent 身份安全要验证授权绑定关系
  • 为 AI 工作流建立可追踪的异常契约
  • 微软开放 Copilot 编排底座,迁移成隐忧