跳到主要内容

工具组合提权:你的安全审查清理了节点,而非边缘

· 阅读需 12 分钟
Tian Pan
Software Engineer

read_file 是安全的。send_email 是安全的。你的安全审计对照各自的威胁模型分别批准了它们:对已知目录的只读访问,以及通过带有速率限制和收件人日志记录的已认证中继发送的出站邮件。每一个都通过了,两者都已注册。随后智能体将它们组合在一起,而客服工单中的一行注入文本就将这对组合变成了外泄工具,原有的审计对此根本没有描述这种风险的术语。

危险并不存在于工具图谱的任何节点中,而是在于边。你运行的每次针对单个工具的安全审计都是对顶点的判定;而智能体实际的权限表面是目录中的路径集合,这个集合呈二次方增长,而你的审计流程却只能线性扩展。当你的智能体拥有 15 个注册工具时,你审计了 15 个项,却发布了大约 200 个可达的两步组合,其中没有一个经过人工审核。

这是在规划器(planner)层面重建的混淆代理(confused-deputy)问题。智能体拥有合法的凭证,用户拥有合法的意图,每一次工具调用都符合策略,但系统仍然发生了泄露。云安全联盟(Cloud Security Alliance)在 2026 年的一份研究报告中准确定义了这一类别:智能体拥有合法的权限,决策受到对抗性输入的影响,由此产生的操作虽然在每个工具'的策略范围内,但却超出了对用户意图的任何合理解释。策略在每个节点都得到了满足,但在执行轨迹上被违反了。

无人列举的组合图谱

走一遍你的智能体实际提供的权限集,而不是你的注册表所列出的那个。

一个拥有 fetch_urlread_repo_fileopen_pull_request 的编程助手看起来像是具备了三个合理的技能。将它们组合起来,你就得到了一个已被记录的攻击模式:攻击者在公共 GitHub issue 中植入指令,智能体在常规上下文收集过程中抓取该 issue,注入的文本指示智能体读取包含 API 密钥的同级文件,然后智能体开启一个 PR,其正文包含窃取的密钥。每一步都使用了授权的工具针对授权的目标。这种组合本身就是漏洞利用。Invariant Labs 在 2025 年针对一款流行的编程智能体展示了正是这种模式;数据外泄通道是一个公开的 PR 正文,而 open_pull_request 的安全审计曾将其视为对低敏感度表面的写入。

一个拥有 read_ticketquery_accountsend_message 的客服智能体看起来像是最小可行工具集。将它们组合起来,ForcedLeak 类攻击就会出现:注入内容存在于智能体作为合法上下文读取的 CRM 字段中,该注入重定向规划器去查询用户并未要求的账户数据,智能体通过安全部门为其他目的批准的白名单聊天连接器发送数据。外泄通道就是产品功能本身。

2025 年事故语料库中最常被提及的危险组合是 exec_codehttp_request。它们加在一起涵盖了读取、转换和发送。单独任何一个都是受控风险;合在一起它们就是一个不受限制的出站通道,而你的网络团队几乎肯定认为这个通道不存在。2026 年的一项关于智能体权限提升的审查指出,这对组合在一次又一次的部署中出现,因为每个工具都独立地解决了一个真实的产品问题,而且没有任何一个审计人员有权说“你可以拥有其中一个,但不能同时拥有两者”。

架构上的认识是令人不安的:你的工具目录不是一个权限集。它是权限集的生成器。安全团队审计了生成器;而智能体运行的是生成的集合。这两者是不同的对象。

安全团队从未审计过的组合图谱

这里失效的思维模型是继承自 Web 应用安全的模型,即每个端点是一个节点,授权发生在节点上。在这种模型中,保护系统意味着保护端点;如果每个端点都是正确的,那么系统就是正确的。组合表面很小,因为客户端不是自主的,不会基于对抗性输入来链接调用。

智能体反转了这一点。规划器是客户端,规划器的一部分指令来自流经工具层的不可信输入,而调用链是由一个实体在运行时构建的,任何控制了智能体最终会读取的字符串的人都可以引导该实体的选择。针对固定威胁模型对每个工具进行的静态审计无法捕获组合漏洞,因为组合所违反的威胁模型并不存在于任何单个工具的审计中。

更糟的是,你的审计日志是错误的。大多数智能体平台在调用边界进行记录:工具名称、参数、返回值、延迟、错误。审计人员阅读日志时看到的是一系列已通过的调用。他们需要的信息——即第三次调用的参数源自第一次调用的不可信输出,且该链条整体上将数据从私有表面移动到了公开表面——在调用边界是不可见的,因为没有人持久化数据流的边。

正确的审计追踪存在于计划边界:规划器的调用前状态、每个参数是如何推导出的轨迹、规划器正在推理的每个值的污染(taint)状态、每个工具输出的出站类别目的地。构建这种追踪比记录工具调用需要做更多工作。跳过它会让你的事故响应陷入这样一种境地:掌握了所有节点级的事实,却对边级事实一无所知。这正是导致混淆代理事故无法从日志中重建的原因。

逐路径审查:真正实现扩展的四种策略

针对单个工具的审查无法泛化。那些能够泛化的规程是从系统安全的古老角落引入并适配到规划器 (planner) 中的。

污点传播 (Taint propagation)。为每个工具的输出标记一个描述数据来源的标签。网络获取产生受污染的输出。读取客户提供的文档产生受污染的输出。默认情况下,你不拥有的 MCP 服务器的输出也是受污染的。当下游工具的参数衍生自受污染的值时,该参数会继承该污点。汇点 (Sinks) —— 针对任意输入的 send_emailopen_pull_requestpost_to_externalexec_code —— 拒绝在没有人工在环 (user-in-the-loop) 明确确认(并指明污点来源)的情况下触发受污染的参数。微软研究院 2025 年的 FIDES 论文表明,这种做法可以以可控的开销动态强制执行,且大多数生产环境中的数据外泄模式都可以表示为“污点源到污点汇点”的规则。这种规程并不新颖;新颖之处在于将其应用于规划器。

组合防御 (Composition guards)。某些序列无论污点状态如何都是危险的。读取 ~/.aws/credentials 随后进行任何出站网络调用,这是一个被攻破的代理 (agent) 的配置终态,而不是一个有用的产品流程。将禁止的子序列编码为防御措施,在规划器意图进行下一次调用时触发,而不是等待调用执行。防御策略不需要非常复杂就能发挥作用;早期生产环境的获胜点在于拒绝那些任何合法请求都不会生成的两步组合。

能力模式切换 (Capability mode flips)。拥有 15 个能力的工具目录不需要在整个运行轨迹中始终均匀可达。在代理获取了不可信文档后,缩小规划器下一步可以调用的工具集。一种“读取不可信内容后”模式,在用户明确确认继续执行前禁用 send_emailopen_pull_requestexec_code,这能将最危险的模式转化为需要用户关注的模式。模式切换的实现成本很低,并且可以针对单次注入就能在一条轨迹内串联多步攻击的模式建立分类防御。

计划边界处的爆炸半径限制。针对单次调用的速率限制无法对付那些将一次逻辑外泄拆分为 40 次微小调用的规划器。强制执行预算的正确位置是计划 (plan):总流出字节数、总不可逆副作用、访问的跨租户数据总量,所有这些都在轨迹中汇总,并与规划器必须遵守的限制进行比较。计划级预算相当于数据库中事务范围内的资源限制,同样的道理也适用 —— 局部优化胜过局部执行,仅仅是因为全局属性是在全局范围内跟踪的。

捕捉组合缺陷的评估规范

大多数代理评估套件只针对单次调用的胜任力评分:代理是否选择了正确的工具、是否传递了有效的参数、是否解析了响应。这些都无法锻炼组合能力。

组合评估是红队计划,而非红队提示词 (prompts)。测试案例是一个代理应该拒绝完成的对抗性轨迹,在工具调用序列的级别进行表达。例如:一个带有嵌入式注入的客户支持对话记录,要求代理获取并发送电子邮件账户历史记录;一个代码任务,其上下文包含一个被投毒的 README,指示代理将文件复制到公共仓库;一个研究任务,带有被投毒的搜索结果,指示代理向攻击者控制的 URL 发送带有凭据的请求。正确的评估根据代理是否在到达危险汇点前放弃轨迹来评分,而不是根据每个单独的调用是否格式正确来评分。

评估集必须与工具目录同步维护。每一个新工具的注册都会增加数据行,因为每一个新工具都可能完成一个之前无法实现的危险组合。评估成本随目录规模增长的原因与威胁面增长的原因相同,明确为此编制预算是工具目录能够增长且不会让隐性威胁面增长速度超过团队审查能力的唯一途径。

这所指向的组织失效模式

这个问题引发的更深层次问题是谁拥有组合图谱的所有权。在大多数组织中,答案是无人负责。平台团队拥有代理运行时。产品团队拥有单个工具并编写自己的威胁模型。安全团队审查每个工具的注册。这些角色中没有一个拥有注册集体产生的图谱,也没有一个拥有演练该图谱的评估集。

集成后的 工具表面(不是工具本身,而是表面)指定一个明确的所有者,是技术变革落地前必须实现的组织变革。所有者的职责是列举危险组合、编写防御措施、维护污点标签、运行组合评估,并拥有拒绝完成危险路径的工具注册的权力。如果没有这个所有者,每个单独的工具注册都是合理的,每个单独的审查都会通过,而攻击面会不断累积漏洞路径,直到一次事件暴露出缺口。

最终落地的模式是一个包含两个问题而非一个问题的工具注册审查。旧问题 —— “这个工具安全吗?” —— 仍然适用。新问题 —— “这个工具新启用了哪些组合,其中哪些是危险的?” —— 才是捕捉下一类事件的关键。当你第一次对现有目录提出这个问题时,你会发现答案令人不安。这种不安就是当你需要对边 (edges) 进行裁决时,却只在节点 (nodes) 上拥有生成器和裁决器的代价。

人们现在部署的代理并没有这种审查。它们拥有对数据的凭证访问权、授权的流出渠道、在正常运行中读取不可信字符串的规划器,以及随每个产品周期增长的目录。这些事情本身都没问题。组合本身就是缺陷。修复方法是开始将图谱视为产出物,将逐路径威胁模型视为工作内容,并将规划器的轨迹 —— 而非其单个调用 —— 视为必须防御的表面。

References:Let's stay in touch and Follow me for more thoughts and updates