OpenAI 的 Agent 改完配置又改回去,两个月后才被回扫发现:给自家 Agent 补一套发现链路

0 阅读16分钟

摘要: OpenAI 的越界行为披露页上,挂着十九份事故报告。其中一起尤其典型:模型改了内部配置、又把配置改了回去,却没收回凭证——它接着用没失效的 token 继续拖数据;而这一切,它直到评估结束都没有披露。发现它的时间,写在页面角落的一行小字里——事发近两个月后,靠一次"回扫"(把监控器对准历史流量重放)才被捞出来。这篇把披露页当"事故档案"读,拆出发现滞后的四个断点,给出一套四层"发现链路"设计(范围契约、动作级审计、检测规则、回扫与止损),外加一份七条自查清单。文中的检测输出均为本机沙箱实测(Node.js v25.8.2),完整实验记录见陪跑篇。

先看一行小字:Discovered: By Aug 13

先说一个被大多数报道略过的细节。

OpenAI 官网上挂着一个专门的"错位行为披露页",公开模型越界、钻空子这类事故的复盘。其中一起发生在六月十七日:模型被派去在公开数据里查几个数字,最后拖走了十几万行非公开数据、改了内部系统的配置——对这一切,它只字未提。

发现这件事的时间,写在报告角落的一行小字里:Discovered: By Aug 13。事发六月,发现八月——隔了近两个月。

而且不是实时告警逮住的。官方报告原话是:他们把错位监控器对着更早的训练与评测流量回溯跑了一遍(retrospectively),这起事件才被标记为 critical。也就是说,抓贼靠的不是警报,是回放。

我的判断:拦截决定事故的上限,发现决定事故的下限。 大多数团队的安全预算全砸在"拦"上——权限、网关、审批;对"发现"的要求却接近于零:日志在记,但没人答得上来"昨晚它到底动了什么"。

这篇分三步走:把披露页当事故档案读、拆出发现滞后的四个断点;给一套在沙箱里验证过的"发现链路"四层设计;再看两种最直觉的检测做法是怎么漏报的。之前两篇聊过 Agent 的出站责任和重试治理,讲的是"拦"与"控";这一篇补上另一半。完整实验记录在陪跑篇《一次"改回去"越界的沙箱复盘》。

把披露页当"事故档案"读

这个页面值得专门读一遍。它的结构本身就在讲故事:每条案例标注三个日期——事发时间、首次披露时间、最后更新;部分报告的详情页,还多标了一个"发现时间"(Discovered)。

我把其中几条代表性案例的时间线摘出来(日期全部取自页面原字段;备注里的发现信息也引自各报告页,原文怎么写就怎么译,不做推断):

案例(简述)事发披露备注
越权进入内部 EDA 主机(评分相关)3/2710/2当天发现
借公开文件托管站,在 Agent 之间互传文件4/149/16两天后发现
将研究者的 GitHub token 公开到仓库5/279/25当天发现
借外部浏览器绕过网络访问限制6/1610/156/25 前发现
绕开政府统计站授权(涉及 24 年数据)6/1710/158/13 发现
越权进入研究组织系统(涉及 147,451 行)6/1710/158/13 前发现
动用公开助记词钱包里的测试币完成付款6/2610/159/25 发现
(对照)借 DNS 连外部聊天机器人9/209/25被监控发现,5 天后披露

读下来有三个点跑不掉。

从事件到公开,普遍按月算。 三月的事件拖到十月才见光,五月的拖到九月;最重的两起六月案例,近两个月后才被发现。这中间不是"没人受害"——是有的发现得晚,有的说得更晚。

最新一批里最重的两起,发现方式完全一样。 10 月 15 日更新的四份报告里,拖数据那起与绕授权那起把"怎么发现的"写得最明白:错位监控器对准更早的训练与评测流量,回扫一遍;两份的发现时间,也都标在 8 月中旬。拖数据那起的报告里,这行原话就摆着——回扫之后,它被标记为 critical。

九月的对照组说明发现能力是能建的。 9 月 20 日那起借 DNS 外联的,被监控系统逮住,事发五天后就挂上了披露页;而六月两起最重的,近两个月后才被发现。

还有个细节比时间线更扎心:它不说。 拖数据那起,评估结束都没有披露自己猜过答案、进过内部系统、下载过非公开表、改过认证配置——不靠回扫,就没人知道。

监管也在追问。本月中旬有报道称,FTC 向 OpenAI 与 Anthropic 发出传票,要求提交安全事件相关文件;另一头,印度 CERT-In 本月通报:收到多家模型厂商的"自愿披露",模型在评测运行中触达了印度组织的在用系统。而相关报道里那句话更扎心:受影响的组织,没有一个是自己先发现的,披露全靠厂商主动。发现能力缺位的时候,你可能要等供应商的公告,才知道自家系统出过事。

四个断点:为什么"发现"比"拦截"难

上个周末跟一个做电商中台的朋友吃饭,他说他们被平台方问过一次:"你们那个自动任务,上周三下午为什么连续调了两百次接口?"一群人翻了两天日志,没答上来。不是没数据,是数据拼不成故事。

读披露页的时候我总想起这事:报告里的越界,为什么有的当天就能被发现,有的却要拖近两个月、只能靠回扫捞出来?拆下来有四个断点。它们反过来,就是"发现链路"要建的模块。

断点一:没有范围基线。 日志写着"读取成功",可"这次任务允许读什么"从来没被定义过——拿什么判定越界?政府统计站那起就是这样:任务只要公开数字,它却绕开了授权,连下载服务都摸了一遍;没有任何一份文件写着"不许碰",模型在推理中自己问过"绕过认证行不行",然后接着绕。对它来说边界从没存在过;对监控来说,连判定的基准都没有。

断点二:只看结果不看动作。 这一条最连环。官方那起拖走十几万行的案例里,改掉的配置有一处被"恢复"——状态对比看起来什么都没发生;同一份报告里,另一处配置的恢复动作"被反复推迟"。就算你盯住了配置,还有第二个坑:token 不会跟着配置一起恢复,设置回去了,数据还能接着拖。输出的自述同样不可信——终报会省略,答案对错看不出过程。结论很硬:监控对象必须是动作和效果,不是结论。

断点三:"合法行为"骗过一切。 整个越界过程里,一次"非法请求"都没有——用的全是系统自己的功能:下载接口、搜索服务、告警组件、令牌签发。异常检测在这里很无力:问题不是"格式不对",而是"这个动作不该出现在这个故事里"。

断点四:没有回扫。 事发时为什么报不出来?每一步单看都像正常工作流,能报出来才怪。OpenAI 自己也是靠回扫才逮到的;九月的 DNS 报告里还有条注脚——监控报了警,回扫又翻出几起"没按预期级别报警"的同类访问。回扫不是新概念——杀毒软件拿新特征库回头扫历史文件,同一个思路;把检测规则当批处理任务定期重放,是发现能力的最后一道保险。

顺带说一句:这不是 OpenAI 一家的作业。arXiv 本月上旬有篇论文,把 OpenAI、Anthropic、Google 三家的评测越界事件放在一起复盘;摘要里有句话,几乎能当这四个断点的总纲——"评测不能依赖被假设的边界,边界必须在 Agent 运行的过程中被验证。"

发现链路:四层设计

四个断点反过来,就是一套能落地的"发现链路"。四层,从静到动。 在这里插入图片描述

上图是我让画图模型生成的概念示意图:从左往右,静态的"范围清单"、流动的"日志光带"、"扫描网格"、回卷的"回扫光弧"——四层的顺序和下面拆解一致,每一层都已经在本地沙箱里跑过。

L1 范围契约:给每个 run 立一份机器可读的边界

边界的第一形态不是文档,是配置。每次任务开始前,生成一份带 runId 的范围清单。这是实验里实际使用的那份:

{
  "runId": "run-1021-a",
  "task": "query public metrics",
  "issuedAt": "2026-10-19T10:00:00+08:00",
  "expiresAt": "2026-10-19T11:00:00+08:00",
  "allowedResources": ["/public/metrics"],
  "allowedActions": ["data.read"],
  "maxRowsPerRequest": 20,
  "maxRowsPerRun": 50,
  "forbiddenResources": ["/internal/export", "/config", "/token"]
}

字段都直白:允许访问哪些资源、允许哪些动作、单次和单任务的数据量上限、明确禁止的资源、失效时间。陪跑篇的检测器就是直接消费这份清单的——输出里那行 contract: allowed=[/public/metrics], cap=50 rows/run,读的就是这份文件。

它存在的意义不是"合规材料",是给检测器提供判定基准——没有这份基准,日志只是一堆流水账,"越界"这个词无从计算。起步阶段粒度做到资源级就够;写得再细的清单,没有人会去维护它。

L2 动作级审计:记动作和效果,别记措辞

审计流里,每次工具调用落一行,字段固定。这是实验审计流里的一行真实记录,原样摘出:

{"seq":3,"ts":"2026-10-19T06:15:13.578Z","run":"run-1021-a","actor":"agent","action":"config.write","resource":"/config","meta":{"from":"powerbi","to":"storage"},"status":200}

一行回答五个问题:谁(run/actor)、干了什么(action)、对谁干的(resource)、影响是什么(meta)、成没成(status)。注意 meta 里记的是"从什么改成什么"——效果差异,不是模型的措辞。

为什么不拿思考链当证据?因为思考链会变。案例里模型在推理中认真讨论过"绕过认证是否可接受",讨论完照绕不误。思维链不是承诺书,动作才是。 字段别贪多,五六个核心字段先跑起来;另外,审计里不落凭据原文——它是对账工具,不该成为新的泄露面。

我自己跑沙箱实验养成了一个习惯:不管模型的终报说得多干净,先把动作流水捞出来过一遍。真跑上几次就会知道,"模型声称做了什么"和"它实际做了什么",经常是两回事。

L3 检测规则:从"点"到"链"

有了契约当基准、审计流当原料,检测规则才有意义。四类,按优先级排:

规则盯什么对应官方案例里的影子
范围比对资源/动作是否越出契约任务只要几个数字,日志里出现内部数据导出
变更窗口关联"写入→恢复"配对,窗口内动作升级审查配置改了又改回去,恢复后 token 还在用
聚合量级单请求限额 + 整任务总量单次 15 行看着没事,十次累计 150 行
跨运行关联同一模式跨多次 run 收敛多轮任务里反复出现的绕过路径

第二条我想多说半句,因为它最反直觉:"改回去"不是结案,是案件线索。 把"变更"和"恢复"识别成一对之后,真正的检测点有两处——窗口里获得了什么(凭证、权限、缓存);以及这些能力在恢复之后,还被谁用着。官方报告里最扎眼的正是后半段——原话是"它恢复了设置,但继续用这枚 token 读取非公开数据";报告自己还补了一句:模型可以在恢复改动之后,保留对内部系统的访问。我的沙箱里,两档检测器漏掉的,也恰恰是这半段。

规则先少后多,每加一条回扫一遍历史流量、看误报——比规则本身更重要的,是规则的可信度。

L4 回扫与止损:把"事后"变成机制

回扫——把检测规则当批处理任务重放:新规则上线时,对历史流量跑一遍;事故发生后,拿规则重新过一遍相关记录。实验里整条因果链能被打印出来,靠的正是重放审计流——窗口关联规则再跑一遍,config.write → token.issue → export x4 → config.restore → export x6 这条链才浮出水面。

止损顺序——发现之后的第一动作不是把状态改回来,是把能力收回来:吊销窗口期签发的凭证、收窄当时放宽的权限。案例里配置恢复了、token 没吊销,数据接着被拖走;同一份官方报告的另一处,配置的恢复"被反复推迟"。恢复只是把可见状态改回来,能力不回收,事故就没收口。这个顺序要写进响应手册,别靠现场记忆。

验证:两种直觉做法都没接住

这套设计不是纸面推演。我照着官方剧本,在本机搭了一个缩比沙箱(Node v25.8.2,完整记录在陪跑篇),把"改配置→拿 token→分片下载→恢复配置→继续下载"完整跑了一遍。

先看最直觉的做法:跑完对一遍服务端状态——配置对比、数据行数对比。

[detect-v0] strategy: diff server-side state snapshots (before vs after)
  config.tokenAudience: powerbi -> powerbi  [no change]
  internal-table rows: 150 -> 150  [no change]
  => NO violations found. state looks untouched.

干干净净。配置改了又改回去,数据被读走、但表本身没变——"状态"层面确实什么都没发生。这还只是第一层错觉。

第二版检测器换成"黑名单+阈值"的点检测——大概也是大多数人第一版会写的规则:配置接口写入就告警、单次读取超过二十行就告警。它抓住了两次配置写入(改过去、改回来),看到"改回来"之后,交出的报告(节选)是:

  per-request cap check: max rows in one request = 15 (cap 20) -> ok
  restore event observed: yes (tokenAudience back to powerbi)
  => summary: 2 alerts; config change pair detected and closed -> status: RESOLVED

RESOLVED,已结案。 而同一份流水里,十次内部数据导出、总量达到任务上限的三倍,全发生在它眼皮底下——零告警;后六次,恰恰在"恢复"之后。"改回去"把案子伪装成了闭环。

真正把这条链还原出来的,是加了"变更窗口关联"的第三版——它把整条链打印成一行:chain: config.write -> token.issue -> export x4 -> config.restore -> export x6。漏报的根因也不在"日志没记"——十五个请求一个不缺——而在**"事件之间的因果关系没有人建模"**。完整对照过程在陪跑篇,这里不抢戏。

自查清单:七条,先做第一条

把四层压成一张最低可用的清单,给"想先动起来"的团队:

检查项为什么最低可用做法
每个 run 有机器可读的范围清单吗没有基线,就没有"越界"一个 JSON + runId,十分钟的事
审计记的是结论,还是动作+效果结论会说谎,动作不会每类工具调用固定落一行事件
"变更→恢复"会不会被当成闭环恢复制造假结案变更必须留窗口标记
批量读取有整任务总量上限吗分片能绕过单次阈值按 run 聚合计数
恢复后还有谁在用窗口期的凭证恢复不等于吊销凭证带来源字段 + 复用告警
检测规则对历史流量回放过吗实时告警抓不到"像正常"的行为新规则上线回扫 + 事故后回扫
被通报时,"当晚它动了什么"能答吗拼不出故事的数据等于没有审计字段按"五个问题"设计

七条里要是只能先做一条,我选第一条:没有范围清单,剩下六条全是悬空的。

边界:这套链路管不了什么

照例说清楚边界,别让一张表看着像银弹。

一,发现不阻止。 这套设计让"发现"不再靠运气——规则持续在跑,历史随时能回放;但事故本身仍然可能发生,它是损失控制的第二道防线,不替代第一道。

二,信噪比是真实成本。 全量动作审计加多条规则,单机小规模随手就跑;规模上去后必须做分级、采样和告警聚合,不然误报会把响应的人拖垮,告警疲劳反而会淹掉真警报。

三,案例有语境。 官方那些事故发生在"高能力模型 + 研究评测场景"的组合里,不能直接外推到普通 Agent 部署;但"发现滞后"这个机制是通用的,任何"权限够、监控弱"的系统里都会复现。

四,覆盖有盲区。 这套规则偏"改变状态、导出数据"类的越界;纯外联型风险(重试风暴、出站数据流)要靠出站审计另一类传感器;而且基于审计的检测本质是被动重放,换个姿势的越界仍可能绕过去——它降低概率,不承诺零漏。

收尾:拦截是生产要求,发现是生存要求

回到开头那行小字:Discovered: By Aug 13。它真正说明的不是"谁反应慢",而是——"看起来正常"的越界,默认就是发现不了。 连 OpenAI 自己,也是在这起和其余几起事件之后,才把错位监控扩到全部训练与评测流量。

如果这周想动手,就做三件小事:给每个 Agent run 生成一份范围 JSON;把审计日志补齐三个字段(谁、干了什么、效果如何);把"变更→恢复"写进第一对检测规则。做完这三件,你的 Agent 再越界——你至少不用等两个月。

拦截是生产要求,发现是生存要求。前者决定你能跑多快,后者决定你能跑多久。