工具网关 II — 鉴权层
工具网关 II — 鉴权层
日期: 2026-09-23 阶段: Phase 4 - 自建 Agent 平台×求职冲刺 标签: #gateway-auth #agent-identity #least-privilege
核心问题
Day 100 的工具注册表解决了「LLM 能发现、能调用工具」。但有个致命缺口:任何 agent 都能调任何工具。AML Copilot 调「查制裁名单」是对的,但如果同一个注册表被「客服 agent」复用,它也能调「冻结账户」工具——这就是平台层必须加的第二件:鉴权层。
三个问题:
- agent 调工具,鉴权要分几个方向? 直觉只想到「验 agent 身份」。但 AgentCore 把它拆成 inbound(谁能调这个 agent) 和 outbound(这个 agent 能调哪个下游) 两个完全独立的方向——少考虑一个方向就是一个攻击面。
- 凭证怎么管才算最小权限? 直觉是「给 agent 一个 API key 就行」。但 2026 的共识是 zero standing privileges——agent 静态不持有任何凭证。为什么?
- AgentCore Identity 的 token vault 怎么做的? 它的「绑定键 = workload identity + user ID」设计,是最小权限的物理实现,值得逐步拆解。
关键内容
A. 双向鉴权:inbound 验调用者,outbound 授下游
WebFetch AgentCore Inbound/Outbound Auth 文档(2026),两个方向定义清晰、机制完全不同:
| 方向 | 验的是什么 | 机制 | 失败后果 |
|---|---|---|---|
| Inbound Auth | 调用者→agent/tool/gateway 的访问权 | IAM SigV4 或 JWT/OIDC bearer token(验 iss/aud/client_id/scope claim) | 未授权方调起 agent |
| Outbound Auth | agent→下游资源的访问权 | API key 或 OAuth client(2LO 机器对机器 / 3LO 代用户) | agent 越权调下游、凭证泄露 |
官方原话:「Inbound Auth authenticates and authorizes the caller to get access to an agent, tool, runtime, or gateway using either IAM or JSON web tokens, while Outbound Auth uses either an API key or an OAuth client that allows an agent, tool, or Gateway access to downstream resources.」
inbound 的 JWT 校验是多 claim 联合验证(不是只验签名)——authorizer 配置 discoveryUrl→iss、allowedClients→client_id、allowedAudience→aud、allowedScopes→scope,token 里任一 claim 对不上就拒。这给我的鉴权层一个直接模板:per-agent 的访问策略 = 一组 claim 约束。
反直觉洞察①(agent 的权限边界是「攻击面收窄的第一道闸」,而非「功能开关」):直觉把鉴权当成「让授权用户能用功能」的开关。但对 agent 系统,鉴权的首要作用是收窄 prompt injection 的爆炸半径。WorkOS/Aembit 2026 的反模式分析说得直白:「Single API key routing where the agent backend shares a single, highly privileged service account key... means a single prompt injection inherits maximum blast radius across the entire user base.」一个共享高权限 key 的 agent,被注入一次,攻击者就继承了它的全部权限。per-agent + per-tool 的最小权限不是合规形式,是把「一次注入能干多少坏事」从「全部」压到「这个 agent 这个工具这次会话」。 对 AML 这种能触发「冻结账户」「上报 SAR」的系统,这是生死线。
B. 凭证模型:per-agent / per-tool + zero standing privileges
最小权限在 agent 场景有两层粒度:
凭证矩阵(行=agent,列=tool) 值 = 该 (agent,tool) 对的凭证 + scope
─────────────────────────────────────────────────────────────────────
sanctionsScreen riskScore freezeAccount submitSAR
aml-copilot ✅ read ✅ read ❌ deny ✅ write(HITL)
kyc-agent ✅ read ✅ read ❌ deny ❌ deny
ops-agent ❌ deny ❌ deny ✅ write ❌ deny
注意三条设计:
- per-agent:每个 agent 是独立的 OAuth client(独立 client_id + secret),不共享 service account——身份可归因。
- per-tool:同一 agent 对不同工具有不同权限。AML Copilot 能读名单、但不能冻结账户(冻结是 ops-agent 的事),能写 SAR 但必须经 HITL(day37/day83 的人审持久化)。
- zero standing privileges:agent 静态不持凭证。Zylos/Nango 2026 口径:「agents hold no credentials at rest; when they need access, they request a credential for that specific operation through policy evaluation, and access is automatically revoked when the operation completes.」凭证是 JIT(just-in-time)注入的——用时才发、用完即焚。
这彻底改变了「凭证泄露」的威胁模型:传统 API key 一旦泄露,全程有效;JIT 凭证只在「这个 agent、这个工具、这次会话」的窗口内有效,泄露的利用窗口从「永久」压到「单次操作」。换句话说,最小权限不只是「少给点权限」,而是把权限沿三个维度同时切窄——主体维(哪个 agent)、客体维(哪个工具)、时间维(哪次会话、多久过期)。三维一起收窄,才能让单点失陷的损失可控。
C. AgentCore token vault:绑定键 = workload identity + user ID
AgentCore Identity 的 token vault 是 B 节最小权限的物理实现——它回答一个 B 节没回答的问题:凭证既然不能让 agent 静态持有,那它存在哪、agent 用时怎么取、取的时候怎么保证拿到的是「该拿的那一份」? WebFetch 文档把 outbound 3LO(three-legged OAuth,代用户授权)流程讲得很细,抽成状态机看其内核:
[inbound] 调用者带 JWT bearer token 调 agent
│ Runtime 按 authorizer 验 iss/aud/client_id/scope
▼
[token 交换] Runtime 用 inbound token 换 Workload Access Token
│ (bedrock-agentcore:GetWorkloadAccessTokenForJWT)
│ → 这是 agent 的「工作负载身份令牌」,不是用户凭证
▼
[工具调用] agent 用 Workload Access Token 调 Token Vault
│ (bedrock-agentcore:GetResourceOauth2Token)
├─ vault 里已有该 (workload identity + user ID) 的下游 token?
│ 是 ──► 直接返回缓存 token(免重复授权)
│ 否 ──► 生成 3LO 授权 URL,推给用户 → 用户授权 → vault 缓存
▼
[下游调用] agent 用下游 token 调外部 API(如 Google Drive)
│ token 到期前缓存,到期后重新走授权
▼
[审计] who(workload identity) / whom(user ID) / what(tool) 全程可追
这条流程里有三个值得拆解的设计:一是 token 交换——inbound 的用户 token 不直接拿去调下游,而是先换成 agent 自己的「工作负载身份令牌」(Workload Access Token),把「用户是谁」和「agent 是谁」两个身份解耦;二是 vault 缓存——下游 token 由 vault 托管,用户授权一次后缓存到过期,避免每次工具调用都弹一次授权框(否则 agent 体验无法用);三是 绑定键——这是整个设计的灵魂。官方原话:「AgentCore Identity Service stores the Google access token in the AgentCore Token Vault using the agent workload identity and user ID... as the binding key, eliminating repeated consent requests until the Google token expires.」
反直觉洞察②(最小权限的物理实现是「绑定键」,不是「权限列表」):直觉把最小权限想成「一张 agent 能做什么的清单」。但 AgentCore 的实现揭示更深的一层——凭证在 vault 里是按 (workload identity, user ID) 双键绑定的。这意味着:同一个 agent,替用户 A 调下游拿到的是 A 的 token,替用户 B 调拿到的是 B 的 token,两者物理隔离。一个共享 key 的设计做不到这点——它替所有用户调下游都用同一个凭证,用户 A 的会话被注入就能动用户 B 的数据。「per-user 凭证隔离」比「per-agent 权限列表」更根本:列表管「agent 能调什么」,绑定键管「agent 替谁调、拿到的是谁的凭证」。对 AML,这对应「调查员 A 经手的案子,agent 不能用 A 的凭证去碰 B 的案子」。
凭证类型与 scope 对照:
| 凭证类型 | 用途 | scope 粒度 | vault 绑定键 | 利用窗口 |
|---|---|---|---|---|
| 共享 service key(反模式) | 全 agent 全用户 | 无 | 无 | 永久(泄露=全军覆没) |
| per-agent API key | 单 agent 调单类下游 | agent 级 | workload identity | agent 生命周期 |
| OAuth 2LO client | agent 机器对机器 | agent + scope | workload identity | token 到期 |
| OAuth 3LO + token vault | agent 代用户调下游 | agent + user + scope | (workload identity, user ID) | 用户 token 到期 + 会话 |
设计要点/决策表
| 要点 | 决策 | 理由 |
|---|---|---|
| 鉴权方向 | inbound + outbound 双向独立 | 少一向就是一个攻击面(对照 AgentCore) |
| inbound 校验 | per-agent claim 约束(iss/aud/client_id/scope) | 对齐 AgentCore JWT authorizer 多 claim 联合验 |
| 凭证粒度 | per-agent + per-tool 矩阵 | 收窄 prompt injection 爆炸半径 |
| 凭证持有 | zero standing privileges,JIT 注入用完即焚 | 利用窗口从「永久」压到「单次操作」 |
| per-user 隔离 | 凭证按 (agent, user) 双键绑定 | A 经手的案子 agent 不能用 A 凭证碰 B(绑定键) |
| 危险工具 | freezeAccount/submitSAR 强制 deny 或 HITL | AML 高危操作不交给单 agent 自主 |
对本项目的落地
- 新建
src/agent/platform/gateway/auth.ts:导出AuthPolicy(per-agent claim 约束 + per-tool 凭证矩阵)、checkInbound(agentId, claims) → Decision(验 iss/aud/scope,对照 AgentCore authorizer)、resolveCredential(agentId, toolName, userId) → Credential | Deny(B/C 节:查凭证矩阵 + 模拟 vault 按 (agentId, userId) 双键取凭证)。纯函数、确定性、无真实 OAuth 网络——头注诚实标注「模拟 token vault 的绑定键语义,不做真实 OAuth 流/KMS 加密」。 - 凭证矩阵作数据:把 B 节矩阵写成
src/agent/platform/gateway/credentialMatrix.ts,每个 (agent, tool) 声明{ effect: 'allow'|'deny', scope, requiresHitl }。AML Copilot 对freezeAccount显式deny、对submitSAR标requiresHitl: true——把 day83 的人审持久化挂进鉴权层。 - 与 day100 注册表串联:
PlatformToolRegistry.call(name, args)在校验入参之前先调auth.resolveCredential(callerAgent, name, sessionUser),deny 直接抛McpCallError(FORBIDDEN),不进 handler。鉴权是工具调用流水线的第一道闸(day102 审计会把这个决策落盘)。 - 诚实标注:v1 不实现真实 OAuth 2LO/3LO 流、不接 KMS、不做 token 刷新——只模拟「绑定键取凭证 + per-tool allow/deny + JIT 语义」的形状。真实部署需 AgentCore Identity 或自建 vault + IdP。
zero standing privileges在 v1 体现为「凭证不写进 agent 配置,每次 call 时按矩阵动态 resolve」。
参考资料
- AWS — Authenticate and authorize with Inbound Auth and Outbound Auth(AgentCore Identity 文档):Inbound(IAM SigV4 或 JWT,验 iss/aud/client_id/scope claim)vs Outbound(API key 或 OAuth client,2LO/3LO);Workload Access Token 交换(GetWorkloadAccessTokenForJWT);Token Vault 绑定键 = workload identity + user ID;3LO 缓存免重复授权 (2026)
- AWS — Securing AI agents with Amazon Bedrock AgentCore Identity:token vault 存 OAuth token/client 凭证/API key,KMS 加密,访问控制 scoped to 授权 agent;least-privilege session-scoped token (2026)
- WorkOS — Securing agentic apps: Give your AI agents their own credentials:单共享高权限 key = prompt injection 继承最大爆炸半径;per-agent 独立凭证 + 身份归因 (2026)
- Zylos Research — Agent Authentication & Delegated Access: OAuth Flows, Scoped Tokens:zero standing privileges(静态零凭证);JIT credential injection(用时发、用完焚);2LO/3LO 区分 (2026-04)
- Nango — A complete guide to securing AI agent API authentication (2026):JIT 凭证注入按 policy 评估发放,操作完成即撤销,压缩利用窗口 (2026)
- 本仓库
src/agent/platform/gateway/toolRegistry.ts(day100,call 前插鉴权闸)、docs/aipa/day83-hitl-durable.md(人审持久化,挂 submitSAR)、docs/aipa/day53-risk-gateway.md(P2 风险网关)、docs/aipa/day99-platform-component-boundaries.md(网关三合一边界) (2026-06)
SOTA 检查 (2026-06-11)
- 「inbound/outbound 双向 + token vault」是 2026-06 agent 鉴权的事实标准:AgentCore Identity、Google Agent Identity(per-agent 身份替代共享 service account)、Microsoft Entra Agent ID 三家都把「per-agent 身份 + 双向授权 + 凭证保险库」作为标配,本笔记据 AgentCore 一手文档建模。
- zero standing privileges + JIT 凭证是 2026 升温的主线:Zylos/Nango/Aembit 多家 2026 文章把它列为 agent 安全的核心范式,正因为「共享长效 key」的爆炸半径在 agent 场景被 prompt injection 放大(反直觉洞察①)。本项目 v1 用「动态 resolve 凭证矩阵」模拟其语义。
- 绑定键的 per-user 隔离是合规系统的硬需求:AgentCore 的 (workload identity, user ID) 双键绑定是 2026-06 的清晰实现,对 AML「调查员经手案件隔离」直接适用(反直觉洞察②)。
- 过时认知警示:「给 agent 一个 API key 就够了」过时——共享 key 是 2026 明确的反模式;「鉴权只需验 agent 身份」过时——缺 outbound 方向,下游越权和凭证泄露无防护。
- 待跟踪:OAuth 2.1 在 agent 场景的最终落定(多家 2026 文章按 2.1 写);AgentCore Policy(2026-03-03 GA)与本鉴权层的边界——Policy 做「能不能调」的 Cedar 策略,鉴权层做「用谁的凭证调」,day102 之后需厘清二者是否合并;Microsoft Foundry Memory 仍 preview,跨平台凭证标准未统一。