攻击获得了授权:AI Agent 缺失的安全边界
让一个 AI 智能体(AI Agent)接入 GitHub——或者接入任何一个用持有者凭据(bearer credential)认证的 API 或 MCP 服务器——它就等于拿到了以你的名义行事的权限。凭据未必存放在模型的上下文里;设计良好的系统可能会把它放在 agent 运行时、某个工具或凭据代理(credential broker)中。但下游 API 看到的是同一件事:一个有效的调用方,发出一个被允许的请求。它无法判断这是不是你有意为之的操作。
这个区别很重要。如果 agent 提交了一个错误的 pull request、把私有数据发到了错误的地方、或者以超出你预期的权限委派了一项任务,那么所有传统的授权检查仍可能全部通过。攻击是「被授权」的,只不过授权的人不是你——你从未授权过这次攻击。
服务保护的是它自己,不是你
Simon Willison 把工具型 AI 的危险组合称为「致命三件套」(lethal trifecta):能访问私有数据、会接触到不受信任的内容、以及有对外通信的途径。这三样凑齐,提示注入(prompt injection)就可能升级为数据泄露(data exfiltration)。
模型层的护栏确实有用,我们也应该用。但我不认为概率性的检测应当成为最终的授权边界。模型不一定能分清一条指令是来自操作者,还是藏在网页、邮件、issue 或工具结果里——这两者最终都会作为输入涌进来。
问题不在于 GitHub 的安全做得不够好。GitHub 有细粒度的权限、仓库管控、分支保护、短时有效的安装令牌,授权模型也是多年打磨出来的。站在 GitHub 的角度,agent 就是一个拿着合法权限发出请求的调用方。GitHub 会问:这个调用方被允许执行这个操作吗?它不会问:这个操作是不是人类在当前任务中真正想要的?我们也不该指望 GitHub 来回答第二个问题——它没有那个上下文。
缺失的那道边界,恰恰应该画在人和代表这个人行动的软件之间。机器向来就能持有凭据,但 agent 是另一回事。
服务账户、CI 流水线和后端应用使用凭证已有几十年。因此,说身份系统假定每个凭证持有者都是人类,并不符合事实。真正的区别在于行为。传统自动化通常执行的是某人预先写好的代码。代码可能有 bug,但它的可能动作相对可预测。智能体之所以有用,恰恰是因为它在运行时选择下一步做什么。这个选择可能受到用户请求、检索到的文档、网页、另一个智能体,或前一次工具调用输出的影响。这让最小权限设计变得更加困难。对于 CI 作业,我通常能预测它需要哪个仓库、会执行哪些操作。但对于通用编码智能体,在它实际开始工作之前,我可能无法知道完整的操作序列。我可以给智能体一个范围很窄的令牌,也应该这么做。但静态作用域无法表达我可能想说的所有约束:只在这个仓库里工作。读源代码,但不要读密钥。开 pull request,但不要合并。在外部服务上花费不超过两美元。十分钟后停止。调用破坏性工具之前先问我。如果你委派任务,给子智能体的权限必须小于你自己的。其中一些约束可以用现有产品组合出来,另一些则需要应用特定的策略。棘手的部分在于:当一个智能体委派给另一个智能体时,如何保持这些约束不被打折。委派正是这套模式开始弯折的地方。
智能体越来越多地进行委派:规划者启动一个编码智能体,编码智能体再启动一个研究智能体,研究智能体可能会调用爬虫或其他工具。沿这条链传递下去的权限应当越来越窄,绝不能变宽。RFC 8693 定义的 OAuth 2.0 Token Exchange 可以表示委派,并通过 act 声明标识执行者。它甚至能通过嵌套这些声明来表达完整的委派历史。但就访问控制而言,嵌套历史中更早的执行者只具有参考意义。声明记录的是链条;它本身并不能证明每个子智能体都收到的是父智能体权限的严格子集。而这才是我关心的性质:权限衰减(attenuation)。
如果一个智能体可以读写五个仓库,它就应该能把其中一个仓库的只读访问权委派出去。子智能体还可以再次收窄这项权限。但它绝不应该能添加仓库、恢复写权限、提高预算、延长有效期,或者重置委派深度。我也不希望这个保证依赖于子智能体能正确理解自然语言指令——一次扩权尝试应当在验证时直接失败。
我想要的权限形态
我反复回到的设计问题很简单:我会放心把什么交给一个运行在我无法控制之地的子智能体?我的答案有四个部分:
任务特定限制。 权限应当描述资源、操作、影响、预算、有效期和委派深度。
单向收窄。 持有者可以移除权限,但不能增加权限。
持有证明。 仅复制凭据链是不够的;调用方必须证明自己持有该权限签发时对应的密钥。
可验证记录。 执行之后,我需要看到「批准了什么」和「实际发生了什么」的证据。
这些都不是新的密码学思想。Macaroon 和 Biscuit 令牌已经展示了持有方衰减和基于能力的授权方式。近期的智能体研究也在朝同一方向迈进。South 等人 2025 年的论文《Authenticated Delegation and Authorized AI Agents》用面向智能体的凭据和委派元数据扩展了 OAuth 与 OpenID Connect。2026 年的《Agent Identity Protocol》则在探索跨 MCP 和智能体间通信的可验证身份、衰减授权与来源追溯。
我并不是说没有其他人研究这个问题——有人在研究。Delegent 是我在探索设计空间中某个特定点的尝试:一个体积小、本地运行、可检查的授权层,无需你信任某个托管的第三方服务。
我构建的东西
Delegent 是开源的。它的核心刻意保持朴素:一个只使用标准库的 Go 包,没有数据库、网络依赖或外部运行时。授权逻辑应该小到足以让人阅读、测试和推敲。其基本单元叫做 slip。
slip(授权凭证)是一种带签名的声明,里面写明了各种限制条件:作用范围、影响范围、预算、有效期,以及剩余委托深度。它还和某个智能体的公钥绑定在一起。智能体可以在离线状态下,把自己的 slip 收窄成一份权限更小的子 slip,这条签名链会随请求一起传递。验证方收到后要做的事包括:逐一验证签名;将链条上各层限制取交集;检查有效期和委托深度;拒绝任何试图扩大权限的尝试;并确认调用者确实持有最终 slip 对应的私钥。简化后的协议大致长这样:
pub, priv, _ := protocol.NewKeypair()
slip, _ := protocol.SignSlip(
protocol.SlipBody{/* limits */},
protocol.NewEd25519Signer(priv),
)
chain, _, _ := protocol.Narrow(
parent,
caveats,
childPub,
parentSigner,
nonce,
)
result := protocol.VerifyChain(
chain,
callerPub,
callerSig,
requestBytes,
trustedRoots,
now,
网关放在所有工具的前面。现有智能体通过 MCP 连到网关,而不必再直连每台上游服务器。网关会评估授权请求,对敏感操作请求人工审批,并记录自己的决策。审批渠道可以很灵活:可以是智能体的聊天界面、终端提示符,也可以是发给用户正在盯着的频道的消息。关键在于,审批发生在模型之外,并且在工具调用抵达目的地之前强制执行。
授权(authorization)告诉我「可以做什么」,凭证(receipt)告诉我「实际做了什么」。网关的每个决定都会生成一份带签名的凭证。凭证按主体(principal)做哈希链式串联,删除、重排或改动任何一条记录,都会让校验链断裂。
数据库里有一行写着「某智能体执行了某个操作」,那只是数据库运营方的单方说法;而一条经过签名的凭证链,则能让另一方独立验证。随着智能体系统开始面对合规审查和事故调查,这个区别会变得越来越关键。
欧盟《人工智能法案》(EU AI Act)已经对某些高风险 AI 系统提出了可追溯性和日志记录要求——具体要求和生效时间取决于系统类别,并分阶段实施。姑且不论某个智能体是否落入该监管范围,这个工程问题本身始终值得追问:我能不能重建出,这个智能体被授权做了什么、谁批准的、它实际又做了什么?
OAuth 交换日志只能证明某个 token 被签发过,并不能证明之后的每个动作都还停留在人类的任务级意图之内。凭证链要填的,正是这个缺口。
目前的进展
Delegent 还处于早期阶段。MCP 网关赛道已经很拥挤,成熟产品会支持更多连接器与企业级管控。我并不想去拼功能数量。我关心的问题更聚焦:这个智能体现在被允许做什么?谁授予了这项权限?智能体能否在不扩大自身权限的前提下把任务再委托出去?事后能不能有人独立验证到底发生了什么?
我猜,随着这些智能体运行时间越来越长、接触到的系统越来越关键,并且开始相互委派任务,这些问题会变得更加重要。代码在 github.com/iluxav/delegent。这个协议包只有几百行代码,是纯 Go 编写、零依赖。你可以自己检查,不用只信我的描述。想试用命令行工具,运行:
go install delegent.dev/protocol/cmd/delegent-proto@latest
然后生成一张凭证,试着削弱它的权限,再尝试扩大权限,最后验证生成的收据链。如果这个设计有错误、不完整,或者解决的并不是真正的关键边界,我真心想听听你的看法。现在项目还处于早期,不同的意见会很有价值。