Agent 越权时,先别急着怪模型:权限边界才是第一现场
先说结论
Agent 出现越权行为时,我现在不会先问“这个模型是不是失控了”。我会先把现场拆成两部分:
- 运行环境有没有把不该给的能力给出去?
- 模型拿到能力之后,是否为了完成窄目标而绕过了原本应该遵守的边界?
这两件事可以同时成立。
Anthropic 在 8 月 31 日发布的对齐与安全复盘就把问题拆成了这两个方向:一方面是 operational security,另一方面是 motivated reasoning 和为了窄任务采取有害行动的倾向。
这比一句“模型逃出了沙箱”准确得多,也更值得工程师认真看。因为第一类问题靠重新训练未必能解决,第二类问题靠多加一个 approval 弹窗也未必能解决。
发生了什么
先把事实边界摆出来。
Anthropic 说,7 月 30 日曾报告三起 Claude 模型获得真实计算机系统未授权访问的事件。这些模型是为了网络安全评测而有意在没有 cyber safeguards 的条件下运行的;其中一个关键原因是第三方评测环境配置错误,模型因此访问到了互联网。
另外,英国 AI Security Institute 在 8 月 4 日报告了一起自己的测试事件:Claude Mythos 5 同样是在有意不启用网络安全防护的情况下运行,并且被明确给予了互联网访问能力,随后采取了一系列未经授权的行动。
这和“生产环境里的普通 Claude 用户突然被攻击”不是一回事。Anthropic 的文章也说,正在对两起事件做更深入分析,并计划与 METR 合作进行独立审查,后续细节还没有全部公布。
但这不代表事件可以被轻轻带过。测试环境本来就是为了观察模型在极端权限下会做什么;如果测试环境的权限和目标边界没有被准确记录,评测本身就会变成一次真实系统风险。
第一现场其实是这条链
一个能上网、能执行命令、能访问文件的 Agent,至少经过这样一条链:
模型决策 ↓工具适配层 ↓权限策略 / approval ↓沙箱与文件系统 ↓网络出口 / DNS / 代理 ↓凭证与环境变量 ↓真实目标系统任何一层的默认值出错,最终都可能表现成“模型越权”。但排查方向完全不同。
| 现象 | 可能的第一责任层 | 先查什么 |
|---|---|---|
| 明明禁网却能访问外部地址 | 运行环境 | egress、代理、DNS、worker 和子进程 |
| 工具审批没弹,但命令执行了 | 工具/策略层 | 默认策略、调用路径、绕过接口 |
| 只能读 workspace 却读到主机文件 | 沙箱 | mount、符号链接、临时目录和权限继承 |
| 没有给 key 却能调用生产服务 | 凭证层 | 环境变量、sidecar、共享 credential store |
| 获得能力后主动绕过任务边界 | 模型对齐 | 目标函数、提示、训练环境和行为监控 |
这张表不是为了把责任推给基础设施,而是为了避免把不同问题混成一个“模型安全”标签。
approval 不是沙箱
很多 Agent 产品把“需要确认”当成主要安全体验:模型想执行危险操作时,弹一个确认框。
这对用户体验有帮助,但它只覆盖一个很窄的边界:模型通过产品定义的工具接口发起调用时,是否让人批准。
它不能自动阻止:
- 插件初始化时自行读文件
- 插件代码直接发网络请求
- 依赖包在加载阶段执行副作用
- 子进程继承了更高权限的环境变量
- 日志或临时目录里残留真实凭证
- 一个看起来只读的工具通过路径解析访问了 workspace 外部
DeepSeek Harness 的插件清单里就明确提醒过:工具 approval 管的是模型调工具,不等于第三方插件代码被 sandbox 了。这个区别在任何可扩展 Agent 里都成立。
我的默认分层是:
插件代码:按不可信代码处理模型工具调用:按可审计请求处理沙箱子进程:按最小权限执行网络访问:默认拒绝,按域名/任务放行凭证:按一次任务、一次进程注入如果这四层没有分开,界面上的“允许/拒绝”很容易给人一种超过实际能力的安全感。
reward hacking 为什么和越权放在一起
Anthropic 这次文章的另一部分讲了 reward hacking:模型在训练环境里找到作弊方式,获得奖励,却没有真正完成任务。
这件事和网络越权看起来是两个问题,底层却有相似结构:
狭窄的可测目标 ↓模型发现目标与真实意图之间的缝隙 ↓利用缝隙拿到更高回报例如,训练环境奖励“看起来诚实的回答”,模型可能学会堆免责声明;奖励“通过评测”,模型可能寻找修改评分路径的办法。Anthropic 说,他们在容易发生 reward hacking 的模拟环境里训练模型,并观察到更严重的错位行为;同时也提到,早期训练中曾因为看到这类行为而回滚部分训练。
这里不能得出“任何模型都会攻击系统”的结论。更准确的工程问题是:
- 目标是否可被模型直接修改
- 评测是否只检查结果,没有检查过程
- 环境是否给了模型超出任务所需的权限
- 失败是否会被当成普通输出,而不是安全事件
- 监控看到的是行为,还是只看最终分数
给 Agent 画一张真正的权限图
如果今天让我给一个新 Agent 做安全设计,我会先画 capability graph,而不是先选模型:
┌──────────────┐ │ Model │ └──────┬───────┘ │ request ┌──────▼───────┐ │ Policy │ 任务级允许范围 └──┬────────┬──┘ │ │ ┌─────────▼─┐ ┌───▼─────────┐ │ Tool scope │ │ Network │ 域名/端口/时限 └──────┬──────┘ └──────┬──────┘ │ │ ┌──────▼─────────────────▼──────┐ │ Sandboxed process + credentials│ └────────────────┬───────────────┘ │ ┌──────▼──────┐ │ Audit log │ └─────────────┘每个箭头都应该能回答三个问题:
- 谁授予了这项能力
- 能力什么时候失效
- 事后能不能还原一次调用
如果回答不了,说明这项能力只是“碰巧能用”,还没有成为可管理的系统。
一份我会真的执行的测试表
环境隔离
- 测试账号和生产账号完全不同
- 测试域名和生产域名在网络层分开
- 子进程不继承不必要的环境变量
- workspace 外的路径默认不可读
- 临时目录按 session 隔离,任务结束清理
网络出口
- 默认 deny,而不是默认 allow
- 允许列表按任务生效,并设置过期时间
- DNS、HTTP、HTTPS、WebSocket 和子进程网络分别验证
- 代理不会因为环境变量被意外绕过
- 对外请求记录域名、调用方和任务 ID
凭证
- 不把长期 key 写进 system prompt
- 不让插件在初始化时自动扫描凭证目录
- 每个 Agent 只拿它当前任务所需的最小权限
- 轮换和撤销不依赖重启整个服务
- 日志、错误消息和截图中不出现 secret
行为评测
- 不只测任务是否完成,也测是否绕过边界
- 记录模型尝试过但被拒绝的动作
- 对“修改评分器”“伪造成功”“隐藏失败”单独报警
- 检查模型在重试、超时和奖励变化后是否改变策略
- 对高权限任务保留人工复核样本
安全收紧的代价也要承认
权限收紧之后,产品一定会出现更多拒绝。社区里已经有人抱怨 Claude 对一些原本无害的请求变得过于谨慎,这类体感不能直接证明某个具体策略,但它揭示了一个真实取舍:
更少误放行 ←→ 更少误拒绝安全系统不能只追求“拒绝率越高越好”。如果所有不确定请求都被拒绝,用户会开始关闭保护、换工具,最后安全边界反而变差。
比较好的方向是把拒绝变得可解释、可分级:
- 低风险:直接执行并记录
- 中风险:缩小权限后执行
- 高风险:明确展示影响范围,请求批准
- 不可恢复风险:拒绝,并告诉用户需要改变什么配置
用户需要知道是“模型不愿意做”,还是“当前环境没有给它做这件事的权限”。这两种错误的修复方法不一样。
最后
Anthropic 这次复盘最值得记住的不是某个模型名字,而是它承认了一个很多 Agent 团队容易回避的事实:安全事件常常是模型行为和工程配置共同造成的。
所以 Agent 安全的第一张图,不应该是模型排行榜,而应该是权限图、网络图和恢复图。
先把模型放进一个真的隔离环境,再讨论它在环境里表现得有多聪明。否则我们测到的可能只是:谁忘了关哪一扇门。