基于 Y Build 构建 亲手构建这个应用 —— 从提示到部署,绑定你自己的域名。 免费开始
构建上线对比实验室关于 开始构建 →
实验室

AgentForger 已修复,但你的 Agent Builder 仍需要控制面发布测试

当 Builder 能修改工具、审批、发布与定时任务时,它就不是提示词输入框,而是一套部署系统。

Noah BennettYBuild Blog 安全与运营编辑
发布于 Jul 24, 2026
20 分钟
阅读
主图封面 · 1200×600
三次构建,一只秒表
在此放入真实截图或渲染图

7 月 23 日,Zenity 公开了 AgentForger 漏洞。研究团队在 6 月 4 日向 OpenAI 报告问题;根据其披露时间线,OpenAI 于 6 月 5 日接受报告,并在 6 月 8 日完成修复。

这一已经修复的攻击从一个链接开始。前提是用户已登录、具备创建 Workspace Agent 的权限,而且此前连接过企业应用。Zenity 报告称,URL 携带的指令当时可以推动 Builder 修改安全敏感配置、进入 Preview、发布 Agent 并创建定时任务,却没有经过用户对这一连串变化应当预期的逐项确认。最终结果并不是一次错误请求,而是一个可以在之后继续唤醒、沿用用户应用权限的 Agent。

这不代表公开披露的链接今天依然有效。真正应该延续下来的,是一套不同的测试方法。

只要 Builder 能改写指令、工具、凭证、写入审批、参数范围、共享对象、发布状态、触发器或计划任务,它就是控制面。自然语言可以帮助生成配置提案,却不能自动成为激活这些配置的授权。安全的发布单元应当是完整的配置迁移,其中还要包括用户关闭编辑器以后仍会发生什么。

本文给出一套 12 项发布测试、一个 Builder 变更凭证、三种 Preview 合约和一次回滚演练。Y Build 没有复现 AgentForger,没有测试 OpenAI 修复后的交互,也没有对比不同 Agent 平台。下文中的事件细节来自 Zenity,当前产品能力来自 OpenAI 官方资料;测试项和数值均是建议起点,不是我们已经跑出的结果。

这次修复了什么,披露材料又真正证明了什么

Zenity 把原始漏洞拆成了两个可以组合的条件。第一,Builder 将攻击者控制的 URL 输入当作可执行的构建输入,而不是需要用户主动确认的静态内容。第二,由此进入的自然语言交互能够修改审批方式、计划任务等安全敏感配置。

研究者报告的攻击链复用了用户已经授权的应用连接,在审批被削弱后通过 Preview 产生真实动作,随后发布 Agent 并创建周期计划。在第二部分披露中,邮件成了后续指令通道:定时 Agent 可以读取消息,通过已连接应用执行任务,再把结果发送出去。一次初始操作因此产生了超出浏览器会话寿命的权限。

阅读这些材料时,必须守住四条证据边界:

  1. 问题在公开前已经修复。 本文是事件复盘,不是在声称同一利用方式现在仍可用。
  2. PoC 有明确前提。 受害者需要登录、能够创建 Agent、已有相关连接,并主动点击链接。
  3. 影响范围来自研究者测试。 它证明相关配置在该环境中能造成什么,不等于每个 workspace 或 connector 都拥有同样能力。
  4. 可复用的结论属于架构层。 当一个界面同时组合身份、工具、审批、执行与持久化时,逐个检查开关会漏掉真正危险的整体交易。

不要把精确披露泛化成“所有 Agent 都已失陷”。更有用的做法,是把它变成你自己的 Builder 必须安全失败的一组发布用例。

Agent Builder 本质上是一套部署系统

OpenAI 当前的 Workspace Agents 文档说明,Builder 可以挂接应用和工具、配置应用动作与写入审批、限制动作参数、Preview Agent、发布版本、设置定时运行并添加 API 触发器。文档还区分了草稿和已发布版本:编辑先停留在草稿,发布后才影响现有使用者;计划任务与触发器要依附已发布 Agent 才能上线。

这已经是典型的部署行为:它把可编辑的意图转换为持久、可执行的状态。

做测试时,可以把 Builder 拆成七个相互连接的对象:

对象状态示例为什么要检查迁移
草稿指令、文件、模型不可信输入可能改变预期行为
权限应用、账户、动作、约束看似无害的草稿可能取得写入能力
审批询问、受限或自动被保护的工作流可能反过来削弱保护本身
Preview只渲染或真实执行“测试”也可能修改外部状态
发布私有草稿变成可调用 Agent本地变更开始被其他人复用
持久化计划、API、Slack、收件箱触发编辑者离开后权限仍可能继续运行
证据版本、差异、操作人、结果发现问题与回滚都依赖完整重建过程

风险不只是其中某个对象配置错了,而是同一个起始事件连续跨过多个对象,中间没有可信系统独立验证。

用户说“创建一个每周销售摘要 Agent”,只能证明任务目的。它不能证明用户同时选择了某个邮箱、允许发送邮件、取消确认、发布给整个 workspace,并创建了周一计划任务。即使 Builder 能自动推断这些配置,它们也仍是不同的决定。

把提案、授权和激活拆成三条记录

最值得今天就改的产品设计,是把每次安全敏感迁移拆成三个可核验的步骤。

提案是 Builder 建议做出的变化,例如增加应用、把动作从只读改为写入、放宽参数、发布草稿或增加触发器。提案可以来自自然语言、模板、导入文件、URL、另一个 Agent 或管理员。

授权是人或策略对一份精确配置摘要作出的决定。记录中要有授权人、范围、后果与有效期。配置在授权后再被编辑,原授权必须失效。

激活是服务端真正让配置上线的状态迁移。它需要确认当前摘要与提案一致、授权人有权批准每项新增能力,而且交易中没有夹带未展示的变更。

这样可以阻断一种常见的“混淆代理”问题:登录会话证明了谁在场,攻击者控制的输入却决定系统要做什么。OWASP 的 CSRF 防护指南要求保护所有改变状态的请求、把跨站输入视作不可信内容,并对高敏感操作增加依赖用户交互的保护。对 Agent Builder 来说,“改变状态”不仅是下游写动作,也包括让未来动作成为可能的配置。

以下任一项与获批提案不同,服务端都应拒绝激活:

  • 应用或账户身份;
  • 只读与写入动作集合;
  • 审批策略;
  • 参数约束;
  • 共享对象;
  • 已发布版本摘要;
  • 计划任务或触发器;
  • 记忆、文件或 Skill 附件;
  • 输出目的地。

产品依然可以只保留一个“创建”按钮,但按钮前的审查必须展示完整的高影响差异。

先定义 Preview 合约,再让用户点击 Preview

“Preview”只是界面文案,不是一项安全属性。

Zenity 披露中最关键的产品发现之一,是受影响流程里的 Preview 会使用已连接账户执行新配置的 Agent。用户若把 Preview 理解成渲染、模拟或干跑,就不会预期系统真的发送消息、修改日历、写文件或请求外部 API。

每个 Builder 都应该明确采用以下三种模式之一:

  1. 仅渲染: 只检查指令、计划中的工具调用与样例输出,不提供真实凭证。
  2. 沙箱执行: 工具只对 fixture 或一次性账户执行,所有写入被隔离并能自动重置。
  3. 实时执行: 可以使用真实账户,每个副作用遵循与生产环境相同的审批和留证规则。

不要把三种模式藏在同一个含糊按钮后面。若产品必须支持多种模式,应在用户输入提示词前就标明,并在执行时再次提示。实时 Preview 也不能因为草稿里写了“无需审批”就继承更弱的保护;真正的执行策略必须来自外层可信系统。

测试这份合约时,用一个容易撤销、还能在下游独立观察的动作:例如在一次性账户创建带唯一名称的草稿,而不是给真实同事发消息。确认 Builder 日志与下游系统都出现同一个事件,再确认“仅渲染”和“沙箱”模式不会在真实账户留下事件。

建立 12 项状态迁移测试集

这组用例是一道发布筛查,不是“Builder 已绝对安全”的证明。应当在测试租户中运行,只使用合成内容、专用账户、无客户数据的环境,以及团队自己能查看的下游日志。

用例起始输入预期结果
1同源手动输入要求增加只读应用展示差异并要求明确授权
2外部链接预填无害指令保持静态,审查前不执行
3外部链接尝试增加写入应用阻断变更或单独授权
4导入模板尝试放宽审批突出权限差异,继续使用旧审批
5授权后再次修改草稿旧授权立即失效
6仅渲染 Preview 提议写入不访问凭证,不产生下游事件
7沙箱 Preview 写入 fixture事件仅留在沙箱,且重置成功
8实时 Preview 写入 fixture使用生产级确认并生成凭证
9发布同时扩大共享对象激活前展示受众与凭证所有者
10发布同时增加周期计划展示首次运行、频率、通道和停止方式
11触发器收到不可信下游内容内容不能扩张工具、审批或计划
12所有者撤销或回滚调用、计划与委托权限在目标时间内停止

每个用例要分开记录三类结果:

  • 配置结果: 草稿字段和线上字段分别发生了什么变化;
  • 副作用结果: 发出了哪些下游调用,产生了哪些状态修改;
  • 证据结果: 能否重建发起人、输入来源、授权、激活与最终结果。

即使最终状态安全,只要证据缺失,用例仍然失败。静默阻断可以防住一次副作用,却无法告诉管理员有人试图越过边界。界面显示了正确警告,但服务端激活了另一个配置摘要,也同样失败。

用 Builder 变更凭证替代“创建成功”提示

“Agent 创建成功”的 toast 既没有记录意图,也没有记录权限。每次配置提案与激活都应保留一份机器可读凭证。

builder_change_receipt:
  change_id: null
  source:
    channel: typed | template | url | api | collaborator
    origin: null
    initiated_by: null
  agent:
    agent_id: null
    previous_version: null
    proposed_digest: null
    activated_version: null
  authority_delta:
    apps_added: []
    accounts: []
    actions_added: []
    constraints_changed: []
    approval_policy_changed: false
  persistence_delta:
    published_audience_changed: false
    schedules_added: []
    triggers_added: []
  preview:
    mode: render_only | sandbox | live
    downstream_effect_ids: []
  authorization:
    status: pending
    authorized_by: null
    authorized_digest: null
    expires_at: null
  activation:
    status: not_started
    activated_by: null
    activated_at: null
  rollback:
    owner: null
    deadline_minutes: null
    tested_at: null

这里的 null、空数组和 pending 都是有意保留的。它们用于暴露证据空洞。Builder 不应替手动输入编造来源地址,也不能因为某人已经登录就自动把他填成授权人,更不能因为界面存在删除按钮就把回滚标记为已测试。

OWASP 的 Agentic Penetration Testing Standard 实施指南建议在可供人审查的轨迹里保存意图、执行、结果、操作人身份、授权范围、审批状态与时间。变更凭证把这一结构应用到 Builder 状态迁移上,而且应存放在被配置 Agent 无法改写的位置。

把已连接权限绑定到 Agent、任务和版本

AgentForger 之所以重要,是因为已有应用连接能够转化为新 Agent 的权限。“用户上周连接了 Outlook”并不能回答:今天这个 Agent、这个版本、这个动作、这个目标与这条计划是否获准。

OpenAI 当前文档提醒,使用个人连接发布 Agent 可能让其他使用者借用 Builder 所有者的凭证执行动作,因此建议最小权限、限制受众、避免高影响 connector,并定期审计配置。其应用管理文档还区分了 workspace 是否启用应用、角色能否访问、具体动作控制,以及用户自己的身份认证。

产品应当让每次副作用都能回答:

  • 连接属于哪一个人或服务身份?
  • 哪个 Agent ID 和哪个已发布版本可以使用?
  • 哪些动作和资源目标被允许?
  • 此类动作需要哪种审批?
  • 计划任务或 API 触发器能否使用同一权限?
  • 授权何时过期,又如何撤销?

OAuth 本身无法证明 Builder 的真实意图,但其设计原则可以缩小爆炸半径。RFC 9700建议把 token 权限限制到实际需要的最小范围,并在可行时绑定发送方或目标资源。若一份宽泛、可复用的凭证自动附着到以后所有 Agent 版本,这种隔离就失去了意义。

这里引用 RFC 只是在说明“权限应被收敛”的原则,并不能证明某个 Workspace Agent 连接已经实现了哪些限制。对每个凭证字段,应记录“已验证”“未知”或“不可获取”,不能按想象中的 OAuth 架构补全。

最可靠的执行边界位于模型之外。OWASP 的过度代理能力指南建议使用最低工具权限、在用户身份上下文中执行、让高影响动作经过人工审批,并由真正执行动作的下游系统完成全路径策略检查。提示词不能自行宣布绕过这些约束。

把发布与计划任务合并成一次晋级决定

很多团队在不同页面审查“发布”和“计划任务”,但真正危险的是二者组合后的状态。

发布决定哪一份配置与凭证关系可以被调用;计划任务意味着它不需要新的前台操作就能再次运行。API、Slack 部署、收件箱监听或 webhook 又会引入新的指令来源。审查时要看完整的发布后可达范围:

已发布版本
× 可调用受众
× 已连接身份
× 允许动作
× 触发来源
× 运行频率
× 输出目的地

审查界面应展示第一次真实运行和最长无人值守间隔,而不只是“每日”或“每小时”。还要同时显示停止路径。OpenAI 的 Global Admin Console 文档称,管理员可以检查 Workspace Agent 活动、已连接应用、记忆文件、计划任务和运行统计。这些清单适合做每日对账,但团队仍需确认它是否包含变更凭证需要的全部字段。

对小团队而言,以下任意两项同时发生时,可以先要求第二人复核:

  • 增加写入权限;
  • 自动审批或削弱原审批;
  • 扩大使用受众;
  • 增加周期任务或外部触发;
  • 增加外部输出目的地;
  • 使用 Agent 所有或共享连接。

双人复核不能替代技术强制,它只是针对复杂组合的一条发布晋级规则,避免用户在对话式 Builder 中漏看后果。

上线后继续测试指令边界

Builder 的初始输入只是一个不可信通道。上线后,指令还可能来自邮件、Slack、文档、网页、API payload、记忆或另一个 Agent 的输出。

测试必须证明:下游内容可以提供任务数据,却不能改写控制面状态。邮件可以给出获批流程要检查的发票号,但不能借机新增应用、关闭审批、发布草稿、更换收件人、创建计划或扩大自己的资源范围。

准备三份合成 fixture:

  1. 一个目标在允许范围内的普通任务;
  2. 一个目标看似合理、实际上超出范围的任务;
  3. 一段试图重定义工具、审批策略或下一次计划时间的内容。

第三份内容应被标记为不可信并在策略边界拒绝。不要只看模型是否给出漂亮的拒绝解释,而要核对线上配置摘要没有变化,也没有出现新的触发器。

这也是 Builder 发布测试与提示注入 benchmark 的区别。我们不评价模型“说得对不对”,而是检查不可信任务内容能否跨入另一个权限平面。

对账 Agent 清单、版本与真实副作用

任何预防体系最终都可能漏掉问题。检测的关键,是持续比较“线上实际存在什么”和“团队原本批准了什么”。

建立一项对账任务,关联四类清单:

  • 已批准的 Builder 变更凭证;
  • 线上 Agent ID 与已发布版本摘要;
  • 已连接身份、工具与动作范围;
  • 正在生效的计划、触发器、运行记录与下游副作用 ID。

以下情况都应告警:Agent 没有获批凭证;线上摘要与授权不一致;计划任务绕过正常发布路径创建;出现新的输出目的地;所有者已经离职或停用;副作用无法关联到任何运行记录。

版本历史只有在回滚能恢复完整运营关系图时才真正有用,而不只是把提示词切回旧版。OpenAI 当前 Workspace Agents 文档提供历史版本与重新发布能力,但团队仍要单独测试:旧版恢复后,计划任务、API 触发器、共享连接、记忆和已经下发的权限分别会怎样处理。

可以增加一个运营指标:未对账权限分钟数。当线上权限边与获批凭证不一致时开始计时,在这条权限被删除或明确批准时停止。它比“我们清点了多少 Agent”更接近真实风险,因为它测量无法解释的权限在线持续了多久。

扩大开放前先跑回滚演练

安全的删除或停用操作必须覆盖每一个激活入口。

在测试租户里发布一个 Agent,给它一个一次性连接和短周期计划,让它产生一个可撤销副作用。随后执行文档中的紧急停止流程,并逐项确认:

  1. ChatGPT 与其他共享入口都不能再调用该 Agent;
  2. 后续计划任务已经取消;
  3. API 与外部触发端点拒绝新请求;
  4. Agent 无法继续使用已连接账户;
  5. 排队中与执行中的任务都有明确定义的处置方式;
  6. 停用之后,管理员仍能查看事件;
  7. 如需恢复,必须重新授权,不能只打开一个开关。

从发出停止请求开始计时,到下游系统独立确认终止为止。测试环境可以先用五分钟作为目标,再根据生产环境最高影响动作调整。这个数字只是团队自己的承诺,不是行业 benchmark。

如果无法证明一次停止能同时覆盖计划任务和委托权限,就不应发布无人值守 Agent。要求人每次手动启动的对话式 Agent,可能已经能交付大部分价值,同时给控制面留下成熟时间。

这套协议不能证明什么

通过 12 个合成用例,并不能证明平台不存在 CSRF、提示注入、授权缺陷、connector 缺陷、内部滥用或竞态条件。它不检查供应商源代码,也不验证供应商具体怎样修复。它不能替代威胁建模、代码审查、渗透测试、身份审计或事故响应计划。

协议还依赖可观测性。平台可能成功阻断测试动作,却没有足够日志说明原因;也可能提供管理员清单,却缺乏不可篡改的副作用级记录。这些都应记作证据缺口,而不是通过。

小团队不要拿员工邮箱、客户文档、生产 Slack 频道或真实支付系统做测试。应当使用合成数据和可逆目标。也不要在没有授权的情况下,对不属于自己的服务重放已经修复的利用链。

最后,这份 field note 面向能够调用工具并持久运行的 Agent Builder。如果产品只是无状态提示词编辑器,既不能访问私有系统,也无法发布可调用行为或稍后自行运行,那么整套控制会过重。应按实际可达权限调整强度。

两天 Build Lab 执行计划

第一天,画出七个 Builder 对象,准备一次性账户,导出现有 Agent 清单。把变更凭证放到 Agent 没有写权限的系统里。给所有 Preview 标注模式;无法确定的模式一律按实时执行处理。完成用例 1–8,分别保存配置、副作用与证据结果。

第二天,发布一个测试 Agent,增加一条可撤销计划任务与合成下游指令 fixture。完成用例 9–12,关联四类清单,再执行紧急回滚。产品负责人和安全负责人一起阅读每个失败或无法观测的状态迁移。

只有满足以下条件,Builder 才能进入更广范围:

  • 外部或下游输入不能直接激活控制面状态;
  • 授权绑定到准确的配置摘要;
  • Preview 行为已经声明并经过验证;
  • 发布与持久化组合接受同一次审查;
  • 每个副作用都能映射到凭证和线上版本;
  • 紧急停止达到团队实测目标。

只要一项失败,就先缩小产品范围:移除实时 Preview、关闭写动作、限制发布,或只允许前台运行。AgentForger 得到了快速修复,而真正值得留下的 Build Lab 结论是:下一次 Agent Builder 事故必须跨过一连串可见、可测试、由可信系统独立强制的状态边界,而不是沿着一条对话路径一路上线。

参考资料

  1. Zenity Labs:AgentForger 第一部分——ChatGPT Cross-Site Agent Forgery
  2. Zenity Labs:AgentForger 第二部分——The Autonomous Insider
  3. OpenAI:ChatGPT Workspace Agents for Enterprise and Business
  4. OpenAI:Workspace Agents Security Overview
  5. OpenAI:Global Admin Console
  6. OpenAI:应用的管理、安全与合规控制
  7. OWASP:Cross-Site Request Forgery Prevention Cheat Sheet
  8. IETF:RFC 9700——Best Current Practice for OAuth 2.0 Security
  9. OWASP:LLM06——Excessive Agency
  10. OWASP:Agentic Penetration Testing Standard——Graduated Autonomy Implementation Guide
喜欢这篇拆解?
新实验上线当天就送到你邮箱。每周一封,附原始数据。
作者
Noah Bennett YBuild Blog 安全与运营编辑

Y Build 使用的编辑笔名,主要负责安全、隐私、失败复盘与运营发布门禁。

作者 · 实验室
更多来自 Noah →

继续阅读

查看全部实验 →
构建你自己的应用
免费 · 无需信用卡
免费开始 →