身份与鉴权 · 让 agent 以自己的身份替人行动Identity & Auth · The Agent Acts as Itself, on Someone's Behalf.
企业里的第一个问题不是「它能做什么」,是「它以谁的名义在做」—— agent 需要自己的身份、一份受限的委托,而不是你的万能钥匙。The first enterprise question isn't 'what can it do' but 'in whose name is it acting' — an agent needs its own identity and a scoped delegation, not your master key.
12 分钟 · 初稿 2026.0712 Min · Drafted 2026.07
到上一章为止,你的研究助手一直用一把 ANTHROPIC_API_KEY 加一台机器的全部权限在跑 —— 本地开发这没问题。企业篇从这里开始:当它要在一家公司里、在别人的数据上行动,第一个要回答的问题不是「它能做什么」,是「它以谁的名义在做」。这一章给它一个自己的身份、一份受限的委托、和一张工具级的权限表 —— 全部落在工程里,能跑。Up to the last chapter, your research assistant ran on one ANTHROPIC_API_KEY and the full permissions of one machine — fine for local development. The enterprise part starts here: when it acts inside a company, on other people's data, the first question isn't 'what can it do' but 'in whose name is it acting.' This chapter gives it an identity of its own, a scoped delegation, and a per-tool permission table — all landed in the project, all runnable.先把「企业级」这个词说清。企业级 agent 和「能跑的 agent」的差距,不是更强的模型,而是一整层工程:让它可信地、规模化地、在别人的数据上、留得下审计地行动。这一层有八根支柱 —— 身份鉴权、带权限的检索、对抗性安全、治理审计、常设评测、失败工程、控制平面、多租户。前三根各占一章(本章与接下来两章),其余的已经深化进第 4、10、11 章;最后的 capstone 把八根收成一个可交付的工程。First, the word 'enterprise.' The gap between an enterprise agent and one that merely runs is not a stronger model — it is a whole layer of engineering: acting trustworthily, at scale, on other people's data, with an audit trail left behind. That layer has eight pillars — identity and auth, permission-aware retrieval, adversarial security, governance and audit, standing evaluation, failure engineering, a control plane, and multi-tenancy. The first three get a chapter each (this one and the next two); the rest were deepened into chapters 4, 10, and 11; the capstone folds all eight into one deliverable project.
— I
一把 API key 走天下,死在企业门口One API Key for Everything Dies at the Enterprise Door.
个人项目里,agent 用你的 key、你的 cookie、你的数据库账号 —— 它就是你。企业里这个「就是你」恰恰是问题本身。In a personal project the agent uses your key, your cookies, your database account — it is you. In an enterprise, that 'is you' is precisely the problem.三个死法。归因死:审计日志里全是你的名字,没人分得清哪次查询是你点的、哪次是 agent 自己决定的 —— 出了事故无法追责,也无法只关掉 agent 的那部分。爆炸半径死:你的凭据能做的一切,被注入劫持的 agent 也能做 —— 一把万能钥匙意味着最坏情况就是全部。离职死:agent 挂在某个员工的账号下,那人离职销号,生产里的 agent 一起停摆。OWASP 的 Agentic Top 10 把这类问题单列成一条 ——ASI03「Identity and Privilege Abuse」。5注 5Note 5OWASP · Top 10 for Agentic Applications 2026(2025-12-09 发布)—— ASI03 即「Identity and Privilege Abuse」:agent 的身份与权限被滥用,是官方清单里单列的一条。以官方 PDF 为准。OWASP · Top 10 for Agentic Applications 2026 (released 2025-12-09) — ASI03 is 'Identity and Privilege Abuse': misuse of an agent's identity and privileges, a standalone entry on the official list. Per the official PDF.Three ways it dies. Attribution: the audit log shows only your name, and nobody can tell which query you clicked and which the agent decided on its own — incidents can't be attributed, and you can't revoke just the agent's share. Blast radius: everything your credential can do, a hijacked agent can do — a master key means the worst case is everything. Offboarding: the agent hangs off an employee's account; the employee leaves, the account closes, and the production agent stops with it. OWASP's Agentic Top 10 lists this class as its own entry — ASI03, 'Identity and Privilege Abuse.'5注 5Note 5OWASP · Top 10 for Agentic Applications 2026(2025-12-09 发布)—— ASI03 即「Identity and Privilege Abuse」:agent 的身份与权限被滥用,是官方清单里单列的一条。以官方 PDF 为准。OWASP · Top 10 for Agentic Applications 2026 (released 2025-12-09) — ASI03 is 'Identity and Privilege Abuse': misuse of an agent's identity and privileges, a standalone entry on the official list. Per the official PDF.解法的形状是确定的:agent 要有自己的 workload identity —— 像一个服务、而不是像一个人那样存在。这不用发明新轮子:SPIFFE 就是「在动态异构环境里安全标识软件系统」的开源标准,workload 通过 API 拿到短命的身份文件(SVID)去互相认证。4注 4Note 4SPIFFE —— 「Secure Production Identity Framework for Everyone,一组在动态异构环境里安全标识软件系统的开源标准」。workload identity 的既有标准:workload 通过 API 拿到短命的 SVID 身份文件去互相认证 —— agent 的「自己的身份」不必发明新轮子。SPIFFE — 'the Secure Production Identity Framework for Everyone, a set of open-source standards for securely identifying software systems in dynamic and heterogeneous environments.' The existing standard for workload identity: workloads obtain short-lived SVID identity documents via an API to authenticate to each other — an agent's 'own identity' needs no new wheel.在云上,对应物是 service account / managed identity。要点只有一个:agent 的身份独立于任何员工的身份存在,可以单独授权、单独吊销、单独审计。The shape of the fix is settled: the agent gets its own workload identity — it exists like a service, not like a person. No new wheel needed: SPIFFE is the open standard for 'securely identifying software systems in dynamic and heterogeneous environments,' where workloads obtain short-lived identity documents (SVIDs) via an API to authenticate to each other.4注 4Note 4SPIFFE —— 「Secure Production Identity Framework for Everyone,一组在动态异构环境里安全标识软件系统的开源标准」。workload identity 的既有标准:workload 通过 API 拿到短命的 SVID 身份文件去互相认证 —— agent 的「自己的身份」不必发明新轮子。SPIFFE — 'the Secure Production Identity Framework for Everyone, a set of open-source standards for securely identifying software systems in dynamic and heterogeneous environments.' The existing standard for workload identity: workloads obtain short-lived SVID identity documents via an API to authenticate to each other — an agent's 'own identity' needs no new wheel. On cloud platforms the counterpart is the service account / managed identity. The one essential point: the agent's identity exists independently of any employee's, so it can be separately authorized, separately revoked, separately audited.
— II
委托,不是冒充Delegation, Not Impersonation.
agent 有了自己的身份,第二个问题接上:它替用户干活时,是「变成」那个用户,还是「代表」那个用户?这两个词在标准里有精确的分界。Once the agent has its own identity, the next question follows: when it works for a user, does it become that user, or represent that user? The standards draw this line precisely.RFC 8693(OAuth 2.0 Token Exchange)的原文把线画得很清楚:冒充(impersonation)时,「A 在该上下文里与 B 不可区分」;委托(delegation)时,「A 仍保有自己独立于 B 的身份 —— 明确的是 A 在代表 B 行动」。委托靠 token 里的 act(actor)声明表达:「委托已经发生,行动方是谁」。1注 1Note 1IETF · RFC 8693「OAuth 2.0 Token Exchange」—— act(actor)声明「表达委托已经发生,并标识被委托了权限的行动方」;委托与冒充的区分原文:冒充时「A 在该上下文里与 B 不可区分」,委托时「A 仍保有自己独立于 B 的身份,明确的是 A 在代表 B 行动」。IETF · RFC 8693 'OAuth 2.0 Token Exchange' — the act (actor) claim 'provides a means within a JWT to express that delegation has occurred and identify the acting party to whom authority has been delegated'; impersonation makes A 'indistinguishable from B in that context,' while under delegation 'principal A still has its own identity separate from B, and it is explicitly understood that while B may have delegated some of its rights to A, any actions taken are being taken by A representing B.'对 agent,答案几乎总是委托 —— 审计里要能同时看到两个名字:替谁干(用户),谁在干(agent)。第一节的三个死法,全靠这两个名字分开来救。RFC 8693 (OAuth 2.0 Token Exchange) draws it in so many words: under impersonation, A is 'indistinguishable from B in that context'; under delegation, 'principal A still has its own identity separate from B — any actions taken are being taken by A representing B.' Delegation is expressed by the act (actor) claim in the token: delegation has occurred, and here is the acting party.1注 1Note 1IETF · RFC 8693「OAuth 2.0 Token Exchange」—— act(actor)声明「表达委托已经发生,并标识被委托了权限的行动方」;委托与冒充的区分原文:冒充时「A 在该上下文里与 B 不可区分」,委托时「A 仍保有自己独立于 B 的身份,明确的是 A 在代表 B 行动」。IETF · RFC 8693 'OAuth 2.0 Token Exchange' — the act (actor) claim 'provides a means within a JWT to express that delegation has occurred and identify the acting party to whom authority has been delegated'; impersonation makes A 'indistinguishable from B in that context,' while under delegation 'principal A still has its own identity separate from B, and it is explicitly understood that while B may have delegated some of its rights to A, any actions taken are being taken by A representing B.' For agents the answer is almost always delegation — the audit trail must show both names: for whom (the user) and by whom (the agent). All three deaths in section I are cured by keeping those two names apart.协议层面,这套东西正在标准化成型。OAuth 2.1 把十年的安全最佳实践收编成一份规范(PKCE 全面要求、去掉 implicit flow)—— 注意它截至 2026-03 还是活跃草案,不是 RFC,引用要按草案对待。2注 2Note 2IETF OAuth WG · draft-ietf-oauth-v2-1 —— OAuth 2.1 把 OAuth 2.0 的安全最佳实践收编成一份规范(PKCE 全面要求、去掉 implicit 等)。注意它截至 2026-03 仍是活跃草案(-15,2026-03-02),尚未成为 RFC —— 引用时按草案对待。IETF OAuth WG · draft-ietf-oauth-v2-1 — OAuth 2.1 consolidates OAuth 2.0 security best practices into one spec (PKCE required, implicit removed, etc.). As of 2026-03 it is still an active Internet-Draft (-15, dated 2026-03-02), not yet an RFC — cite it as a draft.而最能说明「agent 世界怎么用它」的一手样本是 MCP:MCP 的授权规范整体是可选项 ——stdio 传输被建议直接从环境取凭据;一旦走 HTTP 并启用授权,措辞立刻变硬:授权服务器 MUST 实现 OAuth 2.1,客户端 MUST 用 PKCE、MUST 带 RFC 8707 的 resource 参数把 token 绑定到目标服务器;MCP 服务器 MUST 校验 token 是发给自己的,且 MUST NOT 接受或转传任何其他 token。3注 3Note 3MCP 规范(2025-11-25)· Authorization —— 授权对 MCP 是可选项;HTTP 传输「SHOULD 遵循本规范」、stdio 传输「SHOULD NOT,改从环境取凭据」。采用时:授权服务器「MUST 实现 OAuth 2.1」(基于 draft-ietf-oauth-v2-1-13),客户端 MUST 实现 PKCE、MUST 带 RFC 8707 resource 参数;MCP 服务器「MUST 校验 token 是专门发给自己的」且「MUST NOT 接受或转传任何其他 token」。截至 2026-07。MCP spec (2025-11-25) · Authorization — authorization is optional for MCP; HTTP transports 'SHOULD conform to this specification' while stdio transports 'SHOULD NOT, and instead retrieve credentials from the environment.' When adopted: authorization servers 'MUST implement OAuth 2.1' (based on draft-ietf-oauth-v2-1-13), clients MUST implement PKCE and MUST send the RFC 8707 resource parameter; MCP servers 'MUST validate that access tokens were issued specifically for them' and 'MUST NOT accept or transit any other tokens.' As of 2026-07.最后这条禁令背后就是下一章的主角 ——confused deputy。At the protocol level this is crystallizing into standards. OAuth 2.1 consolidates a decade of security best practice into one spec (PKCE required across the board, implicit flow removed) — note that as of 2026-03 it is still an active draft, not an RFC; cite accordingly.2注 2Note 2IETF OAuth WG · draft-ietf-oauth-v2-1 —— OAuth 2.1 把 OAuth 2.0 的安全最佳实践收编成一份规范(PKCE 全面要求、去掉 implicit 等)。注意它截至 2026-03 仍是活跃草案(-15,2026-03-02),尚未成为 RFC —— 引用时按草案对待。IETF OAuth WG · draft-ietf-oauth-v2-1 — OAuth 2.1 consolidates OAuth 2.0 security best practices into one spec (PKCE required, implicit removed, etc.). As of 2026-03 it is still an active Internet-Draft (-15, dated 2026-03-02), not yet an RFC — cite it as a draft. The best first-hand sample of 'how the agent world uses it' is MCP: its authorization spec is optional overall — stdio transports are told to take credentials from the environment — but the moment you go HTTP with authorization on, the language hardens: authorization servers MUST implement OAuth 2.1, clients MUST use PKCE and MUST send RFC 8707's resource parameter to bind the token to its target server; MCP servers MUST validate tokens were issued for them and MUST NOT accept or transit any other tokens.3注 3Note 3MCP 规范(2025-11-25)· Authorization —— 授权对 MCP 是可选项;HTTP 传输「SHOULD 遵循本规范」、stdio 传输「SHOULD NOT,改从环境取凭据」。采用时:授权服务器「MUST 实现 OAuth 2.1」(基于 draft-ietf-oauth-v2-1-13),客户端 MUST 实现 PKCE、MUST 带 RFC 8707 resource 参数;MCP 服务器「MUST 校验 token 是专门发给自己的」且「MUST NOT 接受或转传任何其他 token」。截至 2026-07。MCP spec (2025-11-25) · Authorization — authorization is optional for MCP; HTTP transports 'SHOULD conform to this specification' while stdio transports 'SHOULD NOT, and instead retrieve credentials from the environment.' When adopted: authorization servers 'MUST implement OAuth 2.1' (based on draft-ietf-oauth-v2-1-13), clients MUST implement PKCE and MUST send the RFC 8707 resource parameter; MCP servers 'MUST validate that access tokens were issued specifically for them' and 'MUST NOT accept or transit any other tokens.' As of 2026-07. Behind that last prohibition sits the next chapter's protagonist — the confused deputy.落到工程里,我们给研究助手加一格 authz.py:Principal 记三件事 —— agent 自己是谁(agent_id)、替谁干活(on_behalf_of)、属于哪个租户(tenant_id)。这就是委托语义的最小载体:In the project this lands as one new slot, authz.py: a Principal records three things — who the agent itself is (agent_id), on whose behalf it works (on_behalf_of), and which tenant it belongs to (tenant_id). That is the minimal carrier of delegation semantics:
authz.py(工程里可跑的真代码):Principal 与只降权的 token brokerauthz.py (real, runnable code in the project): Principal and a downscope-only token broker
— III
每个工具一个 scope,密钥不进上下文One Scope per Tool; Secrets Never Enter the Context.
第三章说过「工具的 schema 就是它的权限」—— 那是能力面。企业里还差身份面:同一个工具,这个身份能不能用。两张表相乘,才是完整的权限模型。Chapter 3 said 'a tool's schema is its permission' — that's the capability side. The enterprise adds the identity side: may this principal use this tool at all. The two tables multiplied together are the full permission model.工程里这是一张 TOOL_SCOPES 表加一个 check_scope():每个工具登记一个 scope,未登记的默认拒绝;循环在执行任何工具前先查它 —— 排在第九章那道白名单闸的前面。研究助手只有三个只读/沙箱 scope(search:read、web:read、sandbox:exec),一个只拿到前两个的 Principal 跑起来,会看到 run_python 被当场拒绝、拒绝原因进审计。这就把第九章「最小授权」的口号变成了可以按身份配发的东西:不同用户、不同租户、不同任务,发不同的 scope 集合,而工具代码一行不改。In the project this is a TOOL_SCOPES table plus check_scope(): every tool registers one scope, unregistered tools are denied by default, and the loop checks it before executing anything — ahead of chapter 9's allowlist gate. The research assistant holds three read-only/sandbox scopes (search:read, web:read, sandbox:exec); run it with a Principal granted only the first two and you'll watch run_python get denied on the spot, with the reason written to the audit log. Chapter 9's 'least privilege' slogan becomes something you can issue per identity: different users, tenants, and tasks get different scope sets, with zero tool code changed.
loop.py 里的接线:鉴权在白名单之前,拒绝有原因、进审计Wiring in loop.py: authz before the allowlist; denials carry reasons into the audit log
最后是密钥本身。规矩一条:密钥不进模型的上下文 —— 不进 system、不进消息、不进工具描述。上下文会被 trace 记录、被压缩摘要、被 llms 导出,写进去的密钥等于写进了日志。工程里 ANTHROPIC_API_KEY 走环境变量、模型层懒加载,工具拿到的是执行结果而不是凭据;MCP 的 stdio 传输给的建议是同一句话 —— 从环境取凭据。3注 3Note 3MCP 规范(2025-11-25)· Authorization —— 授权对 MCP 是可选项;HTTP 传输「SHOULD 遵循本规范」、stdio 传输「SHOULD NOT,改从环境取凭据」。采用时:授权服务器「MUST 实现 OAuth 2.1」(基于 draft-ietf-oauth-v2-1-13),客户端 MUST 实现 PKCE、MUST 带 RFC 8707 resource 参数;MCP 服务器「MUST 校验 token 是专门发给自己的」且「MUST NOT 接受或转传任何其他 token」。截至 2026-07。MCP spec (2025-11-25) · Authorization — authorization is optional for MCP; HTTP transports 'SHOULD conform to this specification' while stdio transports 'SHOULD NOT, and instead retrieve credentials from the environment.' When adopted: authorization servers 'MUST implement OAuth 2.1' (based on draft-ietf-oauth-v2-1-13), clients MUST implement PKCE and MUST send the RFC 8707 resource parameter; MCP servers 'MUST validate that access tokens were issued specifically for them' and 'MUST NOT accept or transit any other tokens.' As of 2026-07.生产里再加两条:密钥放专门的 secret manager(不进代码库),并让第二节的 broker 只发短命凭据 —— 泄露的钥匙五分钟后是废铁,和泄露一把永久钥匙是两种事故。Finally, the secrets themselves. One rule: secrets never enter the model's context — not the system prompt, not messages, not tool descriptions. Context gets traced, compacted into summaries, exported for agents to read; a key written there is a key written to a log. In the project, ANTHROPIC_API_KEY lives in the environment and the model layer lazy-loads it; tools receive execution results, never credentials. MCP's stdio guidance is the same sentence — take credentials from the environment.3注 3Note 3MCP 规范(2025-11-25)· Authorization —— 授权对 MCP 是可选项;HTTP 传输「SHOULD 遵循本规范」、stdio 传输「SHOULD NOT,改从环境取凭据」。采用时:授权服务器「MUST 实现 OAuth 2.1」(基于 draft-ietf-oauth-v2-1-13),客户端 MUST 实现 PKCE、MUST 带 RFC 8707 resource 参数;MCP 服务器「MUST 校验 token 是专门发给自己的」且「MUST NOT 接受或转传任何其他 token」。截至 2026-07。MCP spec (2025-11-25) · Authorization — authorization is optional for MCP; HTTP transports 'SHOULD conform to this specification' while stdio transports 'SHOULD NOT, and instead retrieve credentials from the environment.' When adopted: authorization servers 'MUST implement OAuth 2.1' (based on draft-ietf-oauth-v2-1-13), clients MUST implement PKCE and MUST send the RFC 8707 resource parameter; MCP servers 'MUST validate that access tokens were issued specifically for them' and 'MUST NOT accept or transit any other tokens.' As of 2026-07. Production adds two more: keep secrets in a dedicated secret manager (out of the repo), and have section II's broker issue only short-lived credentials — a leaked key that dies in five minutes is a different incident from a leaked permanent one.动手 · 让鉴权真的拒绝一次:Hands-on · make authz actually deny something:
01
拿一个降权的 Principal 跑一遍Run once with a downscoped Principal
在 config.py 里把 principal_scopes 改成 ("search:read", "web:read"),离线跑一遍 —— 看 run_python 被拒、任务照样体面收尾(错误回传,ch3 的老朋友)。In config.py, set principal_scopes to ("search:read", "web:read") and run offline — watch run_python get denied while the task still ends gracefully (error-as-result, chapter 3's old friend).
02
用 broker 换一次「只降权」的 tokenMint one downscope-only token from the broker
在 REPL 里 TokenBroker().mint(principal, {"web:read", "admin:write"}) —— 确认拿回来的 scopes 里没有 admin:write:交集语义,升权在结构上不可能。In a REPL, call TokenBroker().mint(principal, {"web:read", "admin:write"}) — confirm the returned scopes exclude admin:write: intersection semantics make privilege escalation structurally impossible.
03
搜一遍你自己 agent 的上下文里有没有密钥Grep your own agent's context for secrets
把你项目里的 system prompt、工具描述、trace 输出全文搜一遍 sk-、Bearer、BEGIN PRIVATE KEY —— 搜到任何一个,就是这一节要你先修的洞。Full-text search your project's system prompts, tool descriptions, and trace output for sk-, Bearer, and BEGIN PRIVATE KEY — any hit is the hole this section wants fixed first.
企业问的第一个问题不是它多能干,
是它以谁的名义在干。.
The enterprise's first question isn't how capable it is —
it's in whose name it acts..
Aklman Library
— 讨论Discussion
讨论Discussion.
评论区初始化中…Initializing comments…
01 / 01
没有匹配结果No matches.
换个关键词,或按 Esc 回到页面Try another keyword, or press Esc to return