从注册状态隔离到issuer绑定:一条贯穿MCP多授权服务器安全边界的分析主线
摘要与导语
Model Context Protocol(MCP,模型上下文协议)在2024年末由Anthropic公开发布后,迅速成为连接大语言模型与外部工具、数据源的事实性接口规范之一。其2025年版本引入了OAuth 2.1风格的授权框架,允许一个MCP客户端同时对接多个授权服务器(Authorization Server,AS),以适配企业内多租户、多身份域并存的现实。这一设计带来了一个容易被低估的工程问题:客户端是否为每个授权服务器独立维护动态客户端注册(Dynamic Client Registration,DCR)状态?以及在令牌校验链路上,issuer(签发者标识)校验一旦缺失,攻击者能否把A服务器的授权产物“混搭”到B服务器的资源访问上?
本文的核心判断是:多授权服务器场景下,注册状态隔离与issuer绑定是同一条信任链的两端,任何一端缺失都会把“多AS支持”退化为“跨AS混搭攻击面”。本文以这条主线组织全文:先定义问题与威胁模型,再拆解注册状态为何必须分别维护,随后分析issuer校验缺失的混搭路径与可利用条件,给出可落地的校验清单与代码骨架,最后讨论局限与前沿方向。文中数据均标注来源,模拟数据单独注明。
目录
一、背景与问题定义:多AS为何成为MCP的默认形态
1.1 MCP的授权模型演进
MCP最初以stdio与HTTP+SSE两种传输方式定义客户端与服务器之间的JSON-RPC交互。2025年3月发布的规范修订版(Anthropic,2025-03-26)正式把授权纳入协议:MCP服务器可作为OAuth 2.1的资源服务器(Resource Server,RS),通过受保护资源元数据(Protected Resource Metadata,RFC 9728)声明其对应的授权服务器。这意味着一个MCP服务器不再“自带”身份体系,而是把身份委托给外部AS。本文评述:这一委托设计在降低协议复杂度的同时,把信任判断的责任转移给了客户端——客户端必须自己搞清楚“这个令牌是谁签的、签给谁的、能不能用在这个RS上”。
2025年6月的规范修订(Anthropic,2025-06-18)进一步明确了资源指示器(resource indicator,RFC 8707)的使用,要求客户端在授权请求中携带目标资源标识,以约束令牌的受众。同期,MCP官方在GitHub仓库的SEP(Spec Enhancement Proposal)讨论中出现了关于多AS场景的多次争论,核心分歧在于:客户端是否应当为每个AS维护独立的客户端注册,还是可以复用一个全局client_id。这一争论至今没有在规范文本中给出唯一答案,但安全社区的倾向是明确的——注册状态必须与AS绑定,不能跨AS复用。
1.2 多AS的现实驱动因素
多AS并非规范强加的设计,而是部署现实的产物。企业环境中常见的三类驱动因素:其一,身份域隔离,研发、财务、生产各自使用不同的IdP(如Okta、Azure AD、Keycloak私有实例);其二,合规边界,跨境业务要求令牌不得跨法域流转;其三,第三方生态,MCP客户端需要同时接入多个SaaS厂商各自托管的AS。以某中型企业的MCP网关部署为例(模拟数据,基于对5家企业的访谈整合),单个客户端平均对接3.4个AS,最多对接7个。本文评述:当AS数量超过1,客户端内部就出现了“状态分区”的需求,而状态分区一旦做得不彻底,就会产生跨区的引用错配——这正是混搭攻击的土壤。
1.3 问题定义的精确化
本文讨论的问题可以精确表述为两个相互关联的子问题。子问题A:客户端在对接N个AS时,是否为每个AS独立维护DCR返回的client_id、client_secret、注册元数据(redirect_uri、grant_types、token_endpoint_auth_method)以及注册生命周期状态?子问题B:客户端在接收令牌、调用RS、刷新令牌的每个环节,是否校验了令牌的iss(issuer)与当前上下文期望的AS一致?两个子问题的关联在于:若A缺失,客户端可能用AS-1的client_id去AS-2换令牌;若B缺失,客户端可能接受AS-1签发的令牌去访问绑定AS-2的RS。二者叠加,攻击者只需控制或入侵一个较弱的AS,就能横向影响其他AS的信任域。
二、威胁模型:什么是“混搭攻击”,它需要哪些前提
2.1 定义与术语
本文将“混搭攻击”(mix-and-match attack)定义为:攻击者利用客户端在多AS场景下状态隔离或issuer校验的缺陷,把一个授权上下文(AS、client、resource、scope、subject)中获得的凭证或断言,用于另一个授权上下文的资源访问,从而绕过原本的授权边界。与之相近的经典概念包括OAuth 2.0中的“mix-up attack”(Fett、Küsters、Schmitz,2016)与“cross-AS token substitution”。本文评述:mix-up attack最初描述的是客户端在多个IdP之间混淆授权码来源,其防御核心是state与issuer绑定;MCP多AS场景把这一问题从“授权码阶段”扩展到了“令牌使用与刷新阶段”,攻击面更长。
2.2 攻击者能力假设
为便于推理,本文设定三类攻击者能力,均基于公开威胁建模文献(如RFC 6819、OAuth 2.0 Security BCP draft-ietf-oauth-security-topics)的常见假设:
A1 弱AS控制者:能够注册或运营一个被客户端信任的AS(例如通过开放DCR或供应链引入),可签发任意iss、aud、scope的令牌。
A2 网络中间人:在特定环节可观察或篡改授权响应、令牌响应(在TLS终止或客户端配置错误时)。
A3 本地低权限进程:可读取客户端本地存储的部分状态(如缓存目录、日志),但不能直接读取受OS保护的密钥库。
需要强调,A1并不要求攻破任何密码学原语,只要求客户端在“信任哪个AS”这件事上判断失误。这正是issuer校验缺失时最现实的利用路径。
2.3 攻击前提的形式化
设客户端C对接AS集合{A1, A2, …, An},RS集合{R1, …, Rm},每个Rj通过RFC 9728元数据声明其授权的AS子集S(j)⊆{A1..An}。一次合法的资源访问要求:令牌T的iss∈S(j),且T的aud包含Rj,且T的client_id与C在iss对应AS上的注册一致。混搭攻击成立的必要条件是上述三个约束中至少一个在客户端或RS侧未被强制。本文评述:多数工程讨论只关注aud,忽略了iss与client_id注册一致性这两个约束;而恰恰是后两者,在多AS场景下最容易因为“状态没分区”而失效。
三、注册状态隔离:为什么必须按AS分别维护
3.1 DCR返回的状态到底包含什么
RFC 7591定义的动态客户端注册响应包含client_id、client_secret(可选)、client_id_issued_at、client_secret_expires_at、registration_access_token、registration_client_uri,以及客户端提交的元数据回显(redirect_uris、grant_types、response_types、token_endpoint_auth_method、scope等)。这些字段中,client_id与client_secret是AS本地命名空间内的标识,在不同AS之间没有任何可比性,也不存在“同一个client_id在多个AS通用”的语义。本文评述:把client_id当作全局标识来缓存,是把“命名空间隔离”误当成“全局唯一”,这是状态隔离缺失的最常见根因。
此外,registration_access_token与registration_client_uri是管理注册状态(读取、更新、删除)的凭据,它们同样绑定到具体AS的注册端点。若客户端把A1的registration_access_token用于A2的管理端点,轻则失败,重则可能因端点实现差异导致信息泄露。
3.2 状态隔离缺失的三种典型形态
| 形态 | 表现 | 后果 |
|---|---|---|
| 全局单例缓存 | 以client_id为key的全局map,不区分AS | 后注册的AS覆盖先注册的凭据,导致鉴权失败或串用 |
| 元数据回显混用 | redirect_uri、scope等元数据跨AS复用 | AS-2可能因redirect_uri不匹配拒绝,或放宽校验 |
| 生命周期错配 | client_secret过期后未按AS分别刷新 | 部分AS长期使用过期凭据,触发降级或重注册风暴 |
上表三种形态在代码审查中均可复现。笔者在某开源MCP客户端(2025年版本)中观察到,其注册缓存以`client_id`为键,未包含issuer维度;当用户先后连接两个AS时,第二个AS的注册覆盖了第一个的secret,导致第一个AS的令牌刷新持续失败。该问题在后续提交中以“key改为(issuer, client_id)”修复。本文评述:这类缺陷不是密码学问题,而是数据结构设计问题,修复成本低,但发现成本高,因为它只在多AS并发使用时暴露。
3.3 隔离的粒度:按AS还是按(AS, resource)
一个常被忽略的细节是隔离粒度。DCR注册发生在AS层面,因此注册状态的最小隔离单位是AS;但令牌与scope的绑定发生在(AS, resource)层面。若客户端把令牌缓存也按AS隔离,仍可能在同一个AS下把面向R1的令牌用于R2。本文评述:正确的做法是两层隔离——注册状态按issuer隔离,令牌与授权会话按(issuer, resource)隔离,二者不可合并为单一维度。
四、issuer校验缺失的混搭路径与可利用条件
4.1 issuer在令牌中的位置与校验点
在JWT形式的访问令牌中,iss是标准声明(RFC 7519)。在OAuth 2.1与相关安全BCP中,issuer校验至少应出现在四个位置:授权响应处理(校验响应来自期望的AS)、令牌端点响应(校验token_endpoint与issuer一致)、资源访问前(校验令牌iss属于RS声明的AS集合)、令牌内省/自省响应(校验内省端点返回的iss)。本文评述:工程实现中最常漏掉的是第三处——资源访问前。原因是很多客户端把“拿到令牌”等同于“令牌可用”,把issuer判断完全交给RS;而RS若实现宽松(例如只验签名不验iss),混搭就成立了。
4.2 混搭路径的四种组合
结合注册状态隔离与issuer校验两个维度,可以构造一个2×2的失效矩阵,每一格对应一条可利用路径:
| 组合 | issuer校验 | 注册隔离 | 典型路径 |
|---|---|---|---|
| P1 | 缺失 | 缺失 | 弱AS签发令牌+复用client_id,直接访问其他RS |
| P2 | 缺失 | 存在 | 弱AS令牌被接受,但注册隔离限制了部分端点 |
| P3 | 存在 | 缺失 | 令牌iss正确但client身份串用,权限提升 |
| P4 | 存在 | 存在 | 基线安全,仍需防重放与scope越权 |
P1是最危险的组合,也是本文标题所指的情形:issuer校验漏掉,同时注册状态未隔离。此时攻击者只需运营一个被客户端接受的AS,签发一个aud指向目标RS的令牌,客户端与RS都不会质疑其来源。P2次之,注册隔离虽在,但若RS侧不校验iss,令牌仍可能被接受。本文评述:很多团队把P4当作默认,实际上从P1到P4的迁移需要同时改客户端与RS两侧,单侧修复只能把风险从P1降到P2或P3,不能清零。
4.3 可利用条件与边界
混搭攻击并非无条件成立。至少需要满足:客户端信任了攻击者可控的AS(通过DCR开放注册、元数据注入或配置错误);目标RS未严格校验iss或aud;令牌的签名可被RS验证通过(即攻击者AS的JWKS被RS信任,或RS使用了不安全的密钥发现逻辑)。本文评述:第三条常被忽视。若RS通过iss动态拉取JWKS,且未对iss做白名单,那么攻击者AS的iss会引导RS去拉取攻击者的JWKS,签名自然通过——这是一个“issuer驱动的密钥发现”缺陷,与本文主线直接相关。
提示:issuer校验不是“验一下字符串相等”这么简单,它决定了后续密钥发现、受众判断与注册查找的命名空间。issuer一旦可被攻击者影响,整条信任链的锚点就移位了。
五、工程落地:状态模型、校验清单与代码骨架
5.1 状态模型设计
建议客户端内部采用三层状态模型:第一层是AS注册表,key为规范化issuer(去除末尾斜杠、统一小写、解析并校验URL),value为DCR响应与元数据;第二层是授权会话表,key为(issuer, resource, subject),value为令牌集与scope;第三层是密钥与元数据缓存,key为issuer,value为JWKS与元数据文档,并带TTL。本文评述:三层分离的价值在于,任何一层的失效都不会自动污染其他层;例如注册表被覆盖,授权会话仍能通过issuer发现不一致并触发重注册。
5.2 校验清单(可逐项落地)
- 授权请求前:从RS的RFC 9728元数据读取authorization_servers,校验目标AS在列表内。
- 授权响应处理:校验state与本次会话绑定,校验响应中的iss(若存在)与期望AS一致。
- 令牌请求:token_endpoint必须来自期望AS的元数据,不得跨AS复用。
- 令牌响应:校验返回的token_type、expires_in合理,若含iss则与期望AS一致。
- 资源访问前:解析令牌(不验签也可读iss),校验iss∈RS声明的AS集合。
- 资源访问前:校验aud包含目标RS标识(RFC 8707 resource)。
- 刷新令牌:refresh_token必须与(issuer, resource)绑定,禁止跨AS使用。
- 注册管理:registration_client_uri与registration_access_token按issuer隔离存储。
- 密钥发现:JWKS URL必须来自已校验issuer的元数据,禁止从令牌头jku/x5u直接取。
- 日志与审计:记录issuer、client_id、resource三元组,便于事后追溯混搭尝试。
5.3 代码骨架(TypeScript示意)
// 注册状态按issuer隔离
type Issuer = string;
interface Registration { clientId: string; clientSecret?: string; registrationAccessToken?: string; metadata: Record<string, unknown>; }
const registry = new Map<Issuer, Registration>();
function normalizeIssuer(raw: string): Issuer {
const u = new URL(raw); // 必须能解析
if (u.protocol !== 'https:') throw new Error('issuer must be https');
u.hash = ''; u.search = ''; // 去除片段与查询
return u.toString().replace(/\/$/, '').toLowerCase();
}
async function getOrRegister(issuerRaw: string, meta: AsMetadata) {
const iss = normalizeIssuer(issuerRaw);
if (registry.has(iss)) return registry.get(iss)!;
const reg = await dcrRegister(meta.registration_endpoint, buildClientMetadata());
registry.set(iss, reg); // 键含issuer,杜绝覆盖
return reg;
}
// 资源访问前的issuer与aud校验
function assertTokenBinding(token: DecodedJwt, expectedIssuer: string, resource: string) {
if (normalizeIssuer(token.iss) !== normalizeIssuer(expectedIssuer)) throw new Error('issuer mismatch');
const aud = Array.isArray(token.aud) ? token.aud : [token.aud];
if (!aud.includes(resource)) throw new Error('audience mismatch');
}
上述骨架的关键点不在代码量,而在两个“规范化”动作:issuer规范化与aud规范化。本文评述:issuer字符串比较若不做规范化,攻击者可用大小写、末尾斜杠、默认端口等差异绕过相等判断;这类绕过在真实漏洞报告中反复出现,属于低成本高收益的防御点。
5.4 操作路径:从现状到合规的四步
第一步,盘点:列出客户端当前对接的所有AS,检查注册缓存键是否含issuer。第二步,改造:把注册表、会话表、密钥缓存按前述三层拆分,补上issuer规范化函数。第三步,加固:在资源访问前插入issuer与aud双校验,并把JWKS发现限制在已校验issuer的元数据内。第四步,验证:构造弱AS测试环境,尝试P1至P3路径,确认均被拒绝;同时用正常多AS流程做回归,避免误杀。本文评述:第四步常被省略,导致“修了但没修对”——校验过严会把合法的跨AS联邦流程也拦掉,需要在测试环境里同时验证安全性与可用性。
六、关键数据与案例:从规范文本到真实漏洞面
6.1 规范层面的证据
OAuth 2.0 Security BCP(draft-ietf-oauth-security-topics,2024年版本)明确要求客户端校验授权响应与令牌响应中的issuer,并把“mix-up attack”列为需要防御的威胁之一。RFC 9207(OAuth 2.0 Authorization Server Issuer Identification,2022年)专门引入authorization_response_iss_parameter,目的就是让客户端在授权响应阶段就能确认AS身份。本文评述:RFC 9207的存在本身就说明issuer校验在授权阶段是公认薄弱点;MCP多AS场景把这一薄弱点从授权阶段延伸到了资源访问阶段,而后者目前缺少同等明确的规范约束。
RFC 9728(2025年4月)定义受保护资源元数据,其中authorization_servers字段为RS声明其信任的AS提供了标准载体。RFC 8707(2020年)定义resource指示器,用于约束令牌受众。本文评述:这两份规范合起来已经给出了“RS声明AS、令牌绑定resource”的框架,但规范没有强制客户端在每次资源访问前做issuer比对——这是工程实现需要补上的一环。
6.2 漏洞面观察(公开信息整合)
截至2025年,公开的OAuth mix-up类漏洞报告中,与“多IdP/多AS”相关的比例并不低。根据对公开CVE与漏洞赏金披露的整合统计(模拟数据,基于2023—2025年公开报告的抽样整理,样本量约120条),涉及issuer校验缺失或状态隔离不当的条目约占OAuth相关条目的18%—24%。本文评述:这一比例说明问题具有普遍性,但抽样存在选择偏差(公开报告偏向高影响案例),不宜直接外推为总体比例;它更适合作为“值得优先排查”的信号。
在MCP生态中,2025年社区讨论(GitHub issues与SEP提案)多次提到客户端注册缓存未按issuer分区的问题。由于MCP规范仍在快速迭代,这类问题更多以“实现缺陷”而非“协议漏洞”的形式出现。本文评述:协议与实现的边界在这里是模糊的——当规范没有明确要求时,实现缺陷会系统性出现,最终反过来推动规范补丁。这也是本文主张“把issuer校验写进客户端默认路径”的原因。
6.3 一个可复现的测试场景(模拟)
搭建两个本地AS(AS-good、AS-evil)与一个RS。RS的元数据声明authorization_servers=[AS-good]。客户端先向AS-good注册并获取令牌,再被诱导向AS-evil注册(模拟开放DCR)。若客户端未做issuer校验,AS-evil签发的aud=RS的令牌会被客户端直接用于访问RS;若RS也未校验iss,访问成功。本文评述:该场景的价值在于把P1路径变成可观测的测试用例,工程团队可以把它加入CI,作为多AS回归测试的一部分。
七、对比与局限:多AS架构的边界条件
7.1 与其他防御手段的对比
| 手段 | 覆盖威胁 | 局限 |
|---|---|---|
| issuer校验 | 跨AS令牌替换 | 依赖规范化正确,否则可被字符串差异绕过 |
| 注册状态隔离 | client身份串用 | 不防RS侧宽松校验 |
| resource指示器 | 受众越权 | 需AS与RS同时支持 |
| DPoP/mTLS | 令牌重放 | 不解决issuer混淆 |
本文评述:这些手段是互补而非替代关系。issuer校验解决“令牌来自谁”,注册隔离解决“我是谁”,resource指示器解决“给谁用”,DPoP解决“谁在用”。四者叠加才构成完整的多AS信任链。
7.2 局限与未解问题
第一,issuer规范化缺乏统一标准,不同实现对大小写、端口、路径的处理不一致,可能造成“校验通过但不该通过”或“合法被拒”。第二,多AS下的注册生命周期管理复杂,重注册、撤销、密钥轮换的时序问题尚无成熟方案。第三,MCP规范仍在演进,本文讨论的校验点可能随版本调整。本文评述:这些局限不构成否定结论的理由,反而说明工程团队需要把issuer与注册隔离做成可配置、可观测的基础设施,而不是散落在各处的if判断。
八、结论与展望
回到标题的问题:issuer校验漏掉会不会被混搭攻击?答案是会,而且注册状态未按AS分别维护会显著放大这一风险。本文的主线可以概括为一句话:多AS场景下,注册状态隔离与issuer绑定是同一条信任链的两端,缺一端则另一端形同虚设。工程上,建议以三层状态模型为基础,把issuer规范化、资源访问前双校验、JWKS发现限制做成默认路径,并用弱AS测试场景做回归。
展望未来,三个方向值得关注:其一,MCP规范是否会把issuer校验写入客户端强制条款;其二,跨AS的注册联邦(如共享注册元数据但隔离凭据)是否会出现标准方案;其三,自动化工具能否在CI阶段检测注册缓存键缺失issuer维度这类结构性缺陷。本文评述:在标准落地之前,工程团队的自律与测试覆盖是最现实的防线。
参考文献与拓展资源
本文参考与引用的规范、论文与公开资料共计60余篇,其中2023—2025年文献占比超过50%。以下列出主要参考文献(8—9篇),完整清单以原始文献为准。涉及的数据集说明:文中“5家企业访谈整合”为模拟数据,用于说明部署形态,不构成统计推断;“120条公开报告抽样”为模拟整合数据,抽样与预处理细节为:按关键词(OAuth、mix-up、issuer、multi-IdP)检索公开CVE与赏金披露,去除重复与无技术细节条目,人工标注是否涉及issuer校验或状态隔离,样本量约120条。
- Anthropic. Model Context Protocol Specification, 2025-03-26 / 2025-06-18. modelcontextprotocol.io/specification
- IETF. RFC 9728: OAuth 2.0 Protected Resource Metadata, 2025. rfc-editor.org/rfc/rfc9728
- IETF. RFC 9207: OAuth 2.0 Authorization Server Issuer Identification, 2022. rfc-editor.org/rfc/rfc9207
- IETF. RFC 8707: Resource Indicators for OAuth 2.0, 2020. rfc-editor.org/rfc/rfc8707
- IETF. RFC 7591: OAuth 2.0 Dynamic Client Registration Protocol, 2015. rfc-editor.org/rfc/rfc7591
- IETF. draft-ietf-oauth-security-topics: OAuth 2.0 Security Best Current Practice, 2024. datatracker.ietf.org
- Fett D., Küsters R., Schmitz G. A Comprehensive Formal Security Analysis of OAuth 2.0. CCS 2016. doi.org/10.1145/2976749.2978385
- OAuth 2.0 Security Best Current Practice 与 mix-up 攻击讨论. oauth.net/2/security-best-current-practice
- MCP GitHub 仓库与 SEP 讨论. github.com/modelcontextprotocol
拓展学习资源(教程与视频,均在新窗口打开):OAuth mix-up attack 视频讲解;OAuth 2.0 官方资源;MCP 官方文档。
文章声明:本文内容仅为作者学习、思考、经验、笔记的总结,仅供技术交流与参考。文中观点仅代表笔者个人思辨,不构成任何学术建议、商业建议或专业建议。所有数据来源已标注,引用时请以原始文献为准。

