<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>OAuth on AI博士 万戈</title>
        <link>https://www.yesmiracle.net/tags/oauth/</link>
        <description>AI博士万戈的技术博客，聚焦 Agentic AI、AI Infra 与 Agent Security，分享 AI 基础设施与工程落地实践。</description>
        <generator>Hugo -- gohugo.io</generator>
        <language>zh-cn</language>
        <managingEditor>admin@yesmiracle.net (万戈)</managingEditor>
        <webMaster>admin@yesmiracle.net (万戈)</webMaster>
        <lastBuildDate>Wed, 21 Oct 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://www.yesmiracle.net/tags/oauth/index.xml" rel="self" type="application/rss+xml" /><item>
        <title>Agent 的身份联邦：SPIFFE 那套工作负载身份正在搬进 Agent 世界，但最后一道关不是身份，是意图</title>
        <link>https://www.yesmiracle.net/post/20261021-agent-identity-federation-spiffe-workload-identity/</link>
        <pubDate>Wed, 21 Oct 2026 00:00:00 +0000</pubDate>
        <author>admin@yesmiracle.net (万戈)</author>
        <guid>https://www.yesmiracle.net/post/20261021-agent-identity-federation-spiffe-workload-identity/</guid>
        <description>&lt;img src="https://www.yesmiracle.net/post/20261021-agent-identity-federation-spiffe-workload-identity/cover.svg" alt="Featured image of post Agent 的身份联邦：SPIFFE 那套工作负载身份正在搬进 Agent 世界，但最后一道关不是身份，是意图" /&gt;&lt;p&gt;你的 Agent 要读 Jira、调内部结算 API、再把结果交给另一个 Agent 去发通知。这一路上它得回答同一个问题好几遍：你是谁，谁允许你这么干。&lt;/p&gt;
&lt;p&gt;过去两年的默认答案很难看：给它一个 API key，或者更糟，给它一个大家共用的 service account。这个答案正在被换掉，换成的方案本身没什么新意，是从云原生搬过来的一个词，工作负载身份（workload identity）。&lt;/p&gt;
&lt;p&gt;我把这条线上的几份一手文档读了一遍，包括 Google Cloud 的 Agent Identity、Anthropic 的 SPIFFE 联邦文档、IETF 那份专门讲 Agent 认证的草案、MCP 七月那版授权规范，还有 ServiceNow 十月中刚发的 AI Passport。读完的结论分两半：身份这件事，搬迁基本完成了；但搬完你会发现问题换了个地方，卡在意图上。&lt;/p&gt;
&lt;h2 id=&#34;service-account-为什么不够用&#34;&gt;Service account 为什么不够用&lt;/h2&gt;
&lt;p&gt;先说清楚为什么不能继续凑合。&lt;/p&gt;
&lt;p&gt;service account 的设计前提是：一个稳定的程序，跑在一台稳定的机器上，代表一个固定的角色。Agent 把这三个前提全打破了。&lt;/p&gt;
&lt;p&gt;它可能是被临时创建出来的，跑完一个任务就销毁。数量可能几百上千，而且会长出来。它常常是在替某个具体的人办事，很少只代表自己。它会跨出你的信任域，去调别人的服务。它还做不了多因素认证，因为没人坐在它前面按指纹。&lt;/p&gt;
&lt;p&gt;在这些前提下，service account 的每个特性都变成了缺点。它是共享的，所以出事之后你查不清是谁干的。它的密钥是长期的，一旦泄露，有效窗口就是它的整个生命周期。它可以被别的程序拿去冒充，因为它本质上就是一把静态钥匙。最关键的一点，它没法表达「代谁办事」——一个共享账号的凭证里，读不出委托链。&lt;/p&gt;
&lt;h2 id=&#34;spiffe-到底给了什么&#34;&gt;SPIFFE 到底给了什么&lt;/h2&gt;
&lt;p&gt;不熟这套东西的话，一句话能说清：SPIFFE 把「你是谁」从「你手里有一把密钥」改成了「平台能证明你是谁」，而且这个证明是短期的、可自动轮换的。&lt;/p&gt;
&lt;p&gt;它有几个原语。身份是一个 URI，长这样 &lt;code&gt;spiffe://trust-domain/path&lt;/code&gt;，前半段是信任域，后半段是工作负载的路径。凭证叫 SVID，可以是 X.509 证书，也可以是一个 JWT，里面的 &lt;code&gt;sub&lt;/code&gt; 就是那个 SPIFFE ID。工作负载不用自己去申请密钥，它调一个本地的 Workload API 就能拿到，签发和轮换都是自动的。能拿到什么身份，由 attestation 决定，也就是平台去验证这个进程实际跑在哪、以什么身份在跑。SPIRE 是这套标准的开源参考实现，挂在 CNCF 下面。&lt;/p&gt;
&lt;p&gt;两种 SVID 的分工值得单独说，因为它对应 Agent 的两种通信形态。X.509-SVID 用来做双向 TLS，适合 Agent 之间、Agent 和服务之间建连的时候互相验明身份，连接一旦建立，身份就绑在通道上了。JWT-SVID 是个可传递的 bearer 令牌，适合放进 HTTP 头去调 API，也适合拿去做跨域联邦，因为它就是一个标准 JWT，任何 OIDC 验证方都验得了。一个 Agent 通常两个都要：用 X.509 跟同伴建连，用 JWT 去调外部服务。&lt;/p&gt;
&lt;p&gt;这里有个设计上的关键取舍：SPIFFE 不解决「你怎么信任另一个信任域」这件事，它把这部分交给联邦。两个信任域要互认，交换的是信任包（trust bundle），也就是对方的签发者公钥集合，而不是互相把对方的每一个工作负载登记一遍。所以身份联邦天然是「我信任你的签发者」，扩展性好得多。&lt;/p&gt;
&lt;h2 id=&#34;适配已经发生而且是多家同时发生&#34;&gt;适配已经发生，而且是多家同时发生&lt;/h2&gt;
&lt;p&gt;我原本以为「SPIFFE 用在 Agent 上」还停在提案阶段。查完发现不是，已经落到产品文档里了。&lt;/p&gt;
&lt;p&gt;Google Cloud 的 Agent Identity 页面，第一句话就是：为每个 Agent 提供强证明的密码学身份，&lt;strong&gt;基于 SPIFFE 标准&lt;/strong&gt;。它专门列了和 service account 的差别：Agent 身份默认不跨工作负载共享，不可被冒充，不允许开发者生成长期密钥，为 Google Cloud 签发的访问令牌会和这个 Agent 唯一的 X.509 证书做密码学绑定，用来防令牌窃取。它配套还有 Agent Registry（登记工具和目标）和 Agent Gateway（执行策略、检查流量）。有个细节我觉得挺关键：配合 Agent Gateway 和 Gemini Enterprise 时，终端用户的凭据由 auth manager 加密、在网关上解密，Agent 全程接触不到明文凭据。&lt;/p&gt;
&lt;p&gt;Anthropic 那边是从联邦那一侧切的。它的工作负载身份联邦支持任何能签发 OIDC 兼容 JWT-SVID 的 SPIFFE 实现，两种接入方式：公网 HTTPS 上的 OIDC discovery 文档，或者直接把 JWKS 注册进去。文档里还写了几个具体约束：JWT-SVID 的 &lt;code&gt;sub&lt;/code&gt; 是 SPIFFE ID，&lt;code&gt;aud&lt;/code&gt; 由工作负载在取令牌时提供，而 Anthropic 额外要求 &lt;code&gt;iss&lt;/code&gt; 和 &lt;code&gt;iat&lt;/code&gt; 两个声明，因为 JWT-SVID 规范没强制它们。它举的例子是 SPIRE 的 OIDC Discovery Provider，负责发布 &lt;code&gt;/.well-known/openid-configuration&lt;/code&gt; 和签名密钥。这个桥一搭起来，JWT-SVID 就跟任何一个 OIDC 令牌一样能被验证。&lt;/p&gt;
&lt;p&gt;标准组织这边的态度更有意思。IETF 有一份草案叫 AI Agent Authentication and Authorization，作者来自 Defakto、AWS、Zscaler、Ping Identity、OpenAI 和 Okta。它的摘要第一句就是：这份文档&lt;strong&gt;不定义新协议&lt;/strong&gt;，而是说明现成的、已经广泛部署的标准（WIMSE 架构加 OAuth 2.0 家族）该怎么用到 Agent 的认证授权上。它引用的 WIMSE，是 IETF 里专门做「多系统环境下的工作负载身份」的工作组，这份 Agent 认证草案就是它的一条输出线。想跟这条线，盯这个工作组比盯单个厂商的产品文档有用。&lt;/p&gt;
&lt;p&gt;厂商侧的落点也在收。ServiceNow 十月十五号发的 AI Passport，定位是「一个新的授权层」，让企业能在多 Agent 工作流的每一个点上，实时控制 Agent 和 MCP 服务器被允许做什么。它强调基于开放规范，任何治理产品、身份提供方或执行点都能为自己的和第三方的 Agent 签发、验证凭证。&lt;/p&gt;
&lt;p&gt;而 MCP 七月那版授权规范，已经把该硬的地方硬了：客户端必须带 &lt;code&gt;resource&lt;/code&gt; 参数（RFC 8707 的资源指示器），服务端必须验证令牌是不是专门发给自己的、必须拒绝 audience 里没有自己的令牌；授权服务器如果不声明支持 PKCE，客户端必须直接拒绝继续。&lt;/p&gt;
&lt;p&gt;规范里还有一条容易被忽略、工程上却最容易出事的要求：禁止令牌透传。意思是 MCP 服务器不能把客户端递给它的令牌原样转发给下游的第三方 API。这个做法看着省事，实际是经典的权限放大：下游只看到「有个合法令牌」，看不到这个令牌本来发给谁、用来干什么。规范要求服务端必须确认自己是令牌的预期接收方，堵的就是这条路。&lt;/p&gt;
&lt;p&gt;把这些摆在一起看，一个判断就出来了：身份这一层已经形成共识，云厂商、模型厂商、平台厂商、标准组织在同一套原语上收敛。收敛的对象，就是从 Kubernetes 那套工作负载身份直接搬过来的。&lt;/p&gt;
&lt;h2 id=&#34;联邦这一步真正解决的是什么&#34;&gt;「联邦」这一步真正解决的是什么&lt;/h2&gt;
&lt;p&gt;我一开始觉得联邦是个锦上添花的词，读完文档发现它是这套东西能规模化的前提。&lt;/p&gt;
&lt;p&gt;如果没有联邦，跨组织调用 Agent 就得做点对点的凭证登记：你的每个 Agent 在我这边登记一遍，我的每个 Agent 在你那边登记一遍。Agent 数量一涨，这个矩阵就爆炸了，而且登记本身又成了新的信任负担。&lt;/p&gt;
&lt;p&gt;联邦把这个矩阵压扁了。我只需要信任你的签发者，剩下的验证走标准流程：你签发的 JWT-SVID，我用你发布的 discovery 文档找到你的公钥，验签，看 &lt;code&gt;sub&lt;/code&gt; 是不是我要放行的那个 SPIFFE ID，再看 &lt;code&gt;aud&lt;/code&gt; 是不是发给我的。信任关系维护在信任域这个层级上，不在单个 Agent 上。&lt;/p&gt;
&lt;p&gt;这才是「身份联邦」这四个字的分量。它让「跨信任域的 Agent 互认」变成一个可运维的工程问题，而不是一个每接一家就要重新谈一次的商务问题。&lt;/p&gt;
&lt;h2 id=&#34;最后一道关意图&#34;&gt;最后一道关：意图&lt;/h2&gt;
&lt;p&gt;到这里，一切看起来都挺顺。但我在读一份安全分析的时候停住了。&lt;/p&gt;
&lt;p&gt;SC Media 五月初有一篇文章，标题直接写着：身份问题修好之后，MCP 的混淆代理问题还在。它的论点是，即使身份层完全正确，Agent 依然可以当混淆代理，因为工具校验的是凭证，不是那个人的意图。&lt;/p&gt;
&lt;p&gt;它给了一张分层的表，我照着整理了一下，加上了每层实际住在哪：&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;控制层&lt;/th&gt;
          &lt;th&gt;它在验什么&lt;/th&gt;
          &lt;th&gt;住在哪&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;认证&lt;/td&gt;
          &lt;td&gt;Agent 是谁&lt;/td&gt;
          &lt;td&gt;身份提供方&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;授权&lt;/td&gt;
          &lt;td&gt;Agent 能访问什么&lt;/td&gt;
          &lt;td&gt;令牌 scope&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;委托&lt;/td&gt;
          &lt;td&gt;行使的是谁的权利&lt;/td&gt;
          &lt;td&gt;委托令牌链&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;持续上下文&lt;/td&gt;
          &lt;td&gt;登录之后发生了什么变化&lt;/td&gt;
          &lt;td&gt;策略引擎&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;意图&lt;/td&gt;
          &lt;td&gt;&lt;strong&gt;用户到底有没有要求过这个动作&lt;/strong&gt;&lt;/td&gt;
          &lt;td&gt;工具边界（这里是空的）&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;前四层现在都有人在做了，而且做得不错。第五层是个洞。&lt;/p&gt;
&lt;p&gt;机制其实不新。混淆代理是三十八年前就被描述过的问题：一个有权限的程序，被一个权限更低的调用方骗着去滥用它的权限。只是今天这个「有权限的程序」变成了一个拿着你生产数据库、邮箱和支付网关访问权的语言模型。而它的输入是自然语言，可以被它读到的任何一段内容改写。&lt;/p&gt;
&lt;p&gt;prompt injection 在这里的表现很特别：它不偷你的凭证，它借用你的凭证。你的 Agent 身份是真的，令牌是合法的，audience 是对的，委托链是完整的。它只是被一份恶意文档说服，去做了一件用户从来没要求过的事。所有身份层的检查都会通过。&lt;/p&gt;
&lt;p&gt;这不是理论。微软给 Copilot Studio 里一个间接 prompt injection 分配了 CVE-2026-21520，我在 NVD 上查了，官方描述是「Copilot Studio 中的敏感信息向未授权方暴露，未认证攻击者可通过网络向量查看敏感信息」，Microsoft 作为 CNA 给了 7.5 的 CVSS 3.1 分。安全圈当时讨论的重点是：给一个 agentic 平台里的 prompt injection 分配 CVE，这件事本身很不寻常。OWASP 在去年十二月的 Agentic 应用 Top 10 里，把这一类列为 ASI01，Agent 目标劫持。&lt;/p&gt;
&lt;p&gt;今年冒出来的三份框架，其实都在往这个方向补。CoSAI 的 MCP 安全白皮书列了十二类、接近四十个威胁。IETF 有一份草案专门讲跨域授权信息共享，给跨信任边界运行的 Agent 定义了威胁模型。还有 CoSAI 四月十七号发的 Agentic 身份与访问管理框架，提了三样东西：签名的 Agent 清单、持续授权、以及保持委托链可审计的 on-behalf-of 令牌。&lt;/p&gt;
&lt;p&gt;所以我的判断是这个：身份层做对之后，攻击面并没有缩小，它被推到了下一层。你从「证明你是谁」走到了「证明你要干的事是有人要你干的」。后面这半句，凭证答不了，因为凭证里没有意图。&lt;/p&gt;
&lt;h2 id=&#34;要落地的话按这个顺序&#34;&gt;要落地的话，按这个顺序&lt;/h2&gt;
&lt;p&gt;如果你现在要给一批 Agent 上身份，我建议的顺序是这样的。&lt;/p&gt;
&lt;p&gt;第一，别用共享的 service account。每个 Agent 一个独立身份，这一条不商量，因为它决定了出事之后你能不能查清是谁干的。&lt;/p&gt;
&lt;p&gt;第二，凭证要短期、要绑定。短期是自动轮换的前提，绑定是防窃取的前提，Google 那边把令牌和 X.509 证书做密码学绑定就是干这个的。&lt;/p&gt;
&lt;p&gt;第三，把委托链做成可审计的。on-behalf-of 令牌链的意义不只是权限收窄，还有事后能还原出「这一串动作是谁委托的」。&lt;/p&gt;
&lt;p&gt;第四，audience 绑定别偷懒。MCP 规范已经把这条写成强制项了，服务端必须拒绝不是发给自己的令牌，这是防令牌转发滥用的基本盘。&lt;/p&gt;
&lt;p&gt;第五，高危写操作单独加一道意图校验。这是前面那个洞的位置。SC Media 那篇给的具体做法是给写操作加签名意图摘要，从风险最大的那几类开始（改邮箱、发起支付、授予权限、重置凭据），再把高影响动作拆成「先起草、再提交」两段。CoSAI 的框架也是同一个方向。&lt;/p&gt;
&lt;p&gt;第五条的难度明显比前四条大。前四条基本是配置和集成的工作，第五条落到最后是改产品流程：你要在用户下单之前多一个确认动作，在系统里多一份能被验证的意图记录，而这两件事都不是运维能自己拍板的。但前四条做完了不做第五条，你得到的是一套很规整、能被绕过的身份体系。&lt;/p&gt;
&lt;h2 id=&#34;写在最后&#34;&gt;写在最后&lt;/h2&gt;
&lt;p&gt;身份联邦这件事，我觉得最值得记的一点，是它示范了一种标准扩散的路径：Kubernetes 世界里被验证过的工作负载身份原语，被原样搬进了 Agent 世界，而且搬的过程里没人觉得需要重新发明一遍。IETF 那份草案的摘要说得很明白，不定义新协议。&lt;/p&gt;
&lt;p&gt;但搬完之后剩下的那道关，也是真的难。身份解决的是冒充，意图解决的是操纵。前者是一个工程问题，后者更接近一个产品设计问题，因为你要回答的是「怎么把用户真实的意愿，变成一个可验证的工件，而不是 prompt 里的一句话」。&lt;/p&gt;
&lt;p&gt;相关阅读：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://www.yesmiracle.net/post/20261008-agent-approval-permission-layer/&#34; &gt;《「在聊天框里点确认」撑不住了：Agent 的审批和权限，正在长成独立的一层》&lt;/a&gt;：那篇讲的是授权与审批层的产品化，可以和这篇的身份层对照看。&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://www.yesmiracle.net/post/20260817-indirect-prompt-injection-deep-dive/&#34; &gt;《Indirect Prompt Injection 深度解析：当 Agent 自动读取网页》&lt;/a&gt;：意图这一关为什么难，根源在间接注入，那篇把机制拆得很细。&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://www.yesmiracle.net/post/20261011-personal-agent-protocol-agentic-commerce-standard/&#34; &gt;《Meta、沃尔玛、Stripe 联手给「AI 替你办事」写登录标准》&lt;/a&gt;：消费侧的 Agent 身份与授权标准，跟这篇的企业侧正好互补。&lt;/li&gt;
&lt;/ul&gt;
</description>
        </item>
        
    </channel>
</rss>
