A2A 权限设计:发现、身份与授权分开判断
文章用虚构货运集成的 Python 策略示例,将 Agent 发现信息、认证身份和业务授权分别处理,避免用单一可信标记放行操作。示例仅允许报价,不允许订舱,且明确未实现传输、卡片签名验证或 SPIFFE 证明。
AI 深度解读
Agent 能发现一个服务、通过身份认证,也不代表它有权调用这个服务的所有能力。原文用一个虚构的货运询价场景,拆开了 A2A 服务发现、调用方身份和业务授权三个判断,反对把它们压成一个 trusted_agent 开关。
它的价值在于把权限边界讲得具体:采购 Agent 可以询价,却不能顺手订舱;身份是真的,越权请求也必须被拦住。不过,这只是认证之后的一小段 Python 策略示例,没有实现或验证完整的 A2A、SPIFFE 部署。
Agent Card 写了什么,不等于调用方能做什么
Agent Card 是服务的能力说明。A2A 支持通过约定位置、注册中心或直接配置发现它,但发现结果不能直接变成受信任配置。对于受控的合作方接入,应先明确认可哪家提供方、哪个端点,再读取对应元数据。
这里有三个独立问题:
- 服务发现回答「这里提供什么能力」。
- 身份认证回答「对面是谁」。
- 业务授权回答「这个身份能对哪个客户、哪票货执行什么操作」。

Agent Card 即使签名有效,也不能自动授予业务权限。还要先确认签名密钥与认可的服务提供方之间存在可信关系。攻击者用自己的密钥签一份能力说明,解决不了合作方接入审核的问题。
原文还提醒核对协议版本:它所引用的当前规范使用 v1.0 结构。旧示例的字段形状可能不同,复制前要核对规范和 SDK。示例刻意没有放入线上传输格式的 Agent Card,避免读者把业务策略误认成协议结构。
身份层也有选择。SPIFFE ID 标识信任域中的工作负载,SVID 用于以可验证方式呈现身份;SPIRE 根据配置的节点和工作负载条件签发身份。但一个名称本身不能证明法律主体,也不能证明访问发票的权限。
例如,spiffe://freight.example/agents/quote-service 可以表示远端货运服务,spiffe://cedar.example/agents/purchasing 可以表示采购调用方。两者都是虚构名称。接收端必须从可信认证层取得身份,不能从请求 JSON、工具参数或模型解释里复制。

SPIFFE federation 的信任包负责支持跨已配置域的身份识别,业务系统仍要决定允许哪个工作负载进入哪个租户。A2A 也不要求使用 SPIFFE;已有 OAuth 集成可以保留,只需把已验证身份明确映射到合作方和业务权限。
询价权限怎样才能挡住订舱请求
示例运行在货运服务端:允许 Cedar 的采购 Agent 为客户 cedar 的货物 C-731 询价,只允许 quote,不允许 book。
模型认为下一步有用,不能成为扩大权限的理由。
下面的代码检查身份、授权记录、操作、客户和货物是否同时匹配:
from dataclasses import dataclass
QUOTE_PEER = "spiffe://cedar.example/agents/purchasing"
@dataclass(frozen=True)
class Peer:
identity: str
@dataclass(frozen=True)
class Mandate:
customer: str
consignment: str
operation: str
valid: bool
def allow_quote(peer, mandate, customer, consignment, operation):
if peer.identity != QUOTE_PEER:
return False, "peer_not_allowed"
if mandate.valid is not True:
return False, "mandate_invalid"
if operation != "quote" or mandate.operation != "quote":
return False, "operation_not_allowed"
if customer != mandate.customer:
return False, "customer_mismatch"
if consignment != mandate.consignment:
return False, "consignment_mismatch"
return True, "allowed"
要盯住 mandate.valid is not True:它要求值严格为布尔值 True,字符串也不能混过去。但这个判断只是示例的有效性入口,不能代替真实授权的有效期检查。
代码成立还依赖一组外部前提。Peer 必须由认证结果构造,Mandate 必须从服务端掌控的授权存储解析,公共接口不能让客户端填写 valid。customer 应来自已认证的合作方关系和可信路由上下文;请求里的货物编号则要先查存储,确认归属。
原文明确说,这段函数不能验证 TLS 证书、执行真实授权到期检查,也不能确定货物所有者。那些检查应在可信输入形成过程中完成。
还有一个没有通用答案的问题:Agent 是以服务自身身份操作,还是代表另一个主体操作?两种关系需要的授权证据不同,不能因为接上 A2A 和 SPIFFE,就假设已经拥有统一的委托格式。
权限测试要用真身份,也要追到任务结束
只测试陌生攻击者被拒绝,覆盖不了业务边界。更有价值的反例是:合作方身份完全合法,但它请求了错误操作或别人的资源。
原文用以下断言展示预期结果:
from dataclasses import replace
peer = Peer(QUOTE_PEER)
mandate = Mandate("cedar", "C-731", "quote", True)
assert allow_quote(
peer, mandate, "cedar", "C-731", "quote",
) == (True, "allowed")
assert allow_quote(
peer, mandate, "cedar", "C-731", "book",
) == (False, "operation_not_allowed")
assert allow_quote(
Peer("spiffe://cedar.example/agents/other-service"),
mandate, "cedar", "C-731", "quote",
) == (False, "peer_not_allowed")
assert allow_quote(
peer, mandate, "another-customer", "C-731", "quote",
) == (False, "customer_mismatch")
assert allow_quote(
peer, replace(mandate, valid=False), "cedar", "C-731", "quote",
) == (False, "mandate_invalid")
assert allow_quote(
peer, mandate, "cedar", "C-999", "quote",
) == (False, "consignment_mismatch")
assert allow_quote(
peer, replace(mandate, valid="false"), "cedar", "C-731", "quote",
) == (False, "mandate_invalid")
最后一个断言尤其关键:valid="false" 是非空字符串,若只检查真假值就可能放行。其他断言分别覆盖订舱、错误工作负载、跨客户、无效授权和错误货物。
这些是合成断言,原文没有提供生产认证或 federation 部署的安全验证结果。集成测试还应覆盖:没有可信信任包的外部凭证、非预期端点,以及请求自报身份与认证身份不一致的情况。
拒绝操作的验收标准是没有产生受保护的外部效果。
接口返回拒绝,但后台仍创建了订舱记录,权限检查就没有守住边界。同时要保留一个应当成功的授权请求,避免把连接故障误判为拦截有效。审计应记录拒绝原因、已验证身份引用及对应外部效果,不能存储秘密凭证。
权限检查也不能止于首次请求。A2A 任务可能持续存在,查询、取消、结果交付和回调都需要检查访问权。任务 ID 应帮助定位授权关系内的记录,不能成为拿到就能通行的凭据。
原文建议将任务绑定到客户和许可操作,并测试跨客户查结果、不拥有任务的 Agent 发起取消。这些只是追加测试建议,短代码没有实现它们。A2A 的 authorisation-required 状态也只是协调补充授权的机制,状态变化本身不会补齐业务许可。
我的判断
我认同它把身份和权限分开处理。对接 Agent 工具时,很容易把「合作方可信」扩大解释为「合作方发来的动作都可信」。示例把权限收窄到具体身份、客户、资源和操作,让这个漏洞变得可检查。
但它更适合用作边界检查样板。固定一个 QUOTE_PEER、一个 valid 标记,表达不了完整的授权生命周期;授权记录如何签发、撤销、过期,真实货物归属怎样解析,原文都没有给出实现。
相比引入 SPIFFE,较小规模的 OAuth 合作方集成可能更容易维护正确。原文提出了这个运维取舍,却没有给出成本或安全性的对照数据,应当把它当作设计经验。已有身份体系能提供可靠认证结果时,优先补齐业务授权映射,通常比为了匹配示例更换身份机制更切题。
能拿来做什么
- 检查 Node 服务或 Agent 工具接口里的身份来源,确保调用方身份由认证层提供,客户归属和授权记录由服务端解析,不能接受模型参数直接指定。
- 为已有权限测试补上合法身份的越权用例,至少覆盖错误操作、其他客户和其他资源,并验证数据库或外部服务没有产生对应效果。
- 检查异步任务的查询、取消和回调接口,让每次访问都回到任务所属客户与授权关系上,避免仅凭任务 ID 返回结果。
- 接入 A2A 前核对规范版本、SDK 与 Agent Card 字段,把服务发现配置、认证配置和业务授权规则分别记录,方便定位接入失败发生在哪一层。