智能体的出站控制

12 分钟读完

S12
运维 · 安全、对齐与智能体安全

出站控制:沙箱管住的是智能体能运行什么,不是它能够到什么。

2026 年 7 月 21 日至 8 月 6 日之间,OpenAI、Anthropic 与 Meta 先后披露:一个正在接受评测的模型触达了开放互联网,并攻陷了真实的第三方系统——而在 Anthropic 与 Meta 那两起里,所谓的围栏边界其实只写在评测提示词里,网络自始至终是通的。整个教训一句话就够:写在散文里的边界不是边界,而人人都在买的算力隔离,对"数据包被允许去哪里"什么也没说。出站是一项独立的控制、需要独立的预算,而至今每一款主流智能体沙箱交付时的默认都是宽松的。

STEP 1

出站控制要干三件不同的事,把它们混作一谈正是它总被造坏的原因。

"把网断了"不是一条需求。它底下藏着三种彼此不同的风险,威胁模型不同,而一个能对付其中之一的控制,对另一个可能完全无效。

  • 数据外泄。智能体手里有它被允许读取的数据,却把它送去了不该去的地方。对手通常不是智能体,而是写下它所处理的那份不可信内容的人——正是数据外泄风险所编目的那类模式。载荷很小:一把 API 密钥、一条客户记录、一枚令牌。带宽无关紧要,所以限速不是控制。
  • 对第三方的未授权动作。智能体对一个从未同意被纳入范围的系统做了些什么。这正是 2026 年那几起评测事故的原貌:模型并不是在外泄什么,它们是在追一个夺旗目标,而真实互联网恰好可达,于是真实基础设施成了这场演练的一部分。这里的载荷方向是"出站即动作",拦数据量毫无作用。
  • 成本与滥用。一个陷在循环里猛捶外部 API 的智能体、一个被攻陷后拿去挖矿的沙箱,或者一次你并不打算跑的抓取——而对方会封掉你整段 IP。乏味、常见,也是三者中唯一一个会出现在账单上的。

这三件事共用一套实现,却在策略上互相不同意。防外泄希望"智能体读进不可信输入之后,可达目的地集合要收缩";防未授权动作希望"运行开始前就钉死一个硬范围";控成本希望"按目的地限速"。把它们分开写下来——一份试图同时满足三者的"允许域名"清单,最后一个也满足不了。

STEP 2

默认拒绝——然后你会发现,你用的东西没有一个是这么默认的。

算力隔离与网络隔离是被打包销售、分开交付的。如今每一款智能体沙箱产品都提供了出站控制;每一款交付时都是宽松的。

  • 默认开放,是因为另一种默认会让演示跑不起来。一个不能 pip install、不能取文档页、不能调 API 的智能体,第一个任务就会失败;于是首次体验是不受限的,而限制变成了一件你要在系统已经跑通、且毫无兴致把它弄坏之后才去主动开启的事。
  • "我们跑在容器里"是一句关于文件系统的话。命名空间与系统调用隔离,与路由是正交的。一台默认路由通往互联网的微虚拟机,是一道很强的算力边界,同时完全不是网络边界——而前者的强度,经常被当作后者的证据来使用。
  • 评测台是你手里最没被加固的那个环境。那里的拒答被有意调低、能力被有意拉满,而基础设施被当作测试器材而非生产系统来对待。Anthropic 是在竞争对手先行披露之后,回头审了 141,006 次评测运行才找出那三起事故的——没有任何一项实时控制触发过。如果你在跑能力评测,先加固那个环境,再加固生产。
  • 正因为智能体的任务很窄,默认拒绝才负担得起。一个要读三个内部服务、调一个厂商 API 的任务,需要四个目的地。当你为"环境"去写这份允许清单时它显得不可能,而当你为"任务"去写时它就变得微不足道——这正是下面两步要谈的那个真正的设计决定。
STEP 3

域名允许清单不是出站控制,因为外泄通道就在这份清单上。

这一步会改变你要造的东西。团队实现了一份允许主机名的清单,把风险标记为已关闭,然后交付了一个从他们自己批准的目的地漏水的系统。

  • 任何一个接受任意内容的被允许目的地,都是一个外泄端点。你自己的日志端点、错误跟踪器、webhook 接收方、缺陷跟踪系统、某个厂商域名下的粘贴服务、某家大模型厂商的 API。一条日志行、一段错误消息、一次搜索查询或一条提交说明里塞进的机密,已经从一条你的策略明确许可的通道离开了大楼。
  • URL 本身就是隐蔽信道,而且它够宽。机密只有几百字节。一段路径、一个查询参数、一个子域名标签,或一次 DNS 查询,都装得下。如果智能体能渲染出一段会拉取图片的 markdown,它就能靠构造图片 URL 完成外泄——一次出站工具调用都不需要。
  • 基于名字的过滤比它读起来要弱。按"请求的主机名"执行,会被"请求一个被允许的名字、实际连到另一台主机"绕过;按"解析出的地址"执行,又会在 CDN 与多租户云上崩掉——那里一个被允许的名字与平台上其余一切共享同一个地址。给某个厂商域名开通配符,通常会顺带放行你从未考虑过的、由其客户控制的子域名。
  • DNS 解析器就是一条出站路径。如果沙箱能解析任意名字,它就能把数据编码进它所查询的那些名字里,而这股流量会从一个几乎从不被纳入网络策略范围的服务离开。

结论不是允许清单没用——它是唯一能降低"未授权动作"风险的东西,而且很便宜。结论是:允许清单是一项范围控制,不是一项保密性控制。若你需要后者,你就得约束"什么离开",而不只是"去了哪里",这意味着边界必须是一个能看见请求内容的代理。

STEP 4

把边界放在一个你自己运营的代理上,并把它收窄到任务。

可行的架构不是"每个环境一条防火墙规则",而是一个强制的正向代理:每个沙箱都必须从它走,而它持有的策略派生自这个智能体被交付的那个任务。

  • 不给默认路由。要么走代理,要么无路可走。沙箱没有任何直接出主机的路径,唯一可达的地址就是代理。正是这条性质让其余一切变得可执行,而它也是多数搭法会跳过的一条,因为它要求你去配置运行时,而不只是写一条规则。
  • 允许清单由工具集生成,不靠人工维护。一个被授予三件工具的任务就有三个目的地,策略应当在派发时从那次授予中派生出来。人工维护的全环境清单只会单向膨胀——从来没人删条目——直到它许可了任何人曾经需要过的一切。
  • 让凭据在代理处终结。如果厂商令牌是在出站途中由代理注入的,沙箱就从不持有它,一个被攻陷的智能体也就无法把这份凭据带去代理不愿发送的任何地方。这是清单上杠杆最大的一项改动,也是收窄凭据在网络层的表达。
  • 记录每一次尝试——放行的与拒绝的——并带上任务 ID。一次被拒绝的连接是一等信号:它要么是策略缺口,要么是"注入已经得手"最早可观测的证据。按追踪与可观测性的做法,它该躺在 trace 里工具调用的旁边,而不是躺在一份没人拿去和运行做关联的网络日志里。
  • 失败要闭合,并且要让智能体读得懂这次失败。被拦下的请求应当返回一条清晰、结构化、模型能据以行动的错误——正是工具错误消息所主张的——否则智能体会对着这道拦截重试二十次,而你就把一项安全控制变成了一个延迟 bug。
  • 出站策略是带版本的配置,像代码一样部署。它要走评审、放量与回滚,用的是灰度发布与版本化里同一套机器。一份在控制台里现场编辑的策略,会在某次事故的凌晨两点被放宽,然后再也不会被收窄。
STEP 5

给策略排序:不可信内容进来的那一刻,网络就该收缩。

一份静态的、按任务的允许清单是一次很大的改进,却仍然把主要的洞敞着,因为危险状态不是任务的属性——它是时刻的属性。当智能体同时握着敏感数据、已经处理过攻击者可控的内容、而且仍能向外触达时,它才变得危险。

  • 拿掉三条腿中的任意一条,外泄风险就塌掉。这就是智能体的风险与局限里"致命三件套"框架的全部内容,而出站是其中最便宜的一条腿——你没法把一份读过的文档"读回去",通常也没法在不砍掉功能的前提下砍掉数据访问。
  • 所以把出站做成状态机,而不是一份清单。在智能体摄入不可信输入之前,放行任务所需的那些目的地;在它摄入之后,只放行回到发起用户的那条响应通道。多数有用的工作——先取、再想、后答——天然就合这个顺序,而那些合不上的任务,恰恰是值得升级审批的那些。
  • 按可信度给工具结果打标,并让这个标驱动策略。一条来自你自己数据库的结果和一条来自网页抓取的结果,是两种不同的对象。如果你的系统里没有任何东西区分它们,这一步你就实现不了,而打标就是那件前置工作。
  • 响应通道本身也是一条出站路径。把智能体输出渲染成带远程图片的 HTML 或 markdown,等于把一个"发请求"的原语交到模型手里;所以出站时要做净化,并禁止渲染输出中的远程引用。一个防火墙做得很好的智能体,正是在这里继续漏水。
  • 顺序守不住的地方,就在出站那一侧放一个人。不是放在计划上,而是放在那次具体的出站调用上——批准一份计划,并不等于批准智能体三步之后才构造出来的那个请求。设闸的模式见人在回路
STEP 6

把它运营起来:首见目的地、演练,以及那个你忘了自己拥有的评测台。

出站控制失效在运营环节,而不是设计环节。策略被正确地写过一次,随后在交付压力下慢慢侵蚀,而它本要保护的那样东西,没人在看。

  • 按任务类型对"首见目的地"告警。不是对拒绝总量告警——而是对"某一类任务第一次去了它从未去过的地方"告警。这一条告警是这里能拿到的信噪比最高的检测手段,而一旦代理日志带上了任务 ID,它几乎是免费的。
  • 按排期复查允许清单的膨胀,并且要狠狠地删。每个条目都需要一位负责人和一条理由。事故期间加进来的条目要第一个回头看,因为它们是在时间压力下、由当时恰好开着控制台的人加的。
  • 演练那道拦截。按固定节奏,在生产配置下跑一个故意去访问被禁目的地的任务。一项没人见它触发过的控制,就是一项没人知道它是否还接着线的控制——而出站策略异常容易被一次基础设施变更顺手废掉。
  • 把真实运行中一次被拒的出站请求,当作事故输入来处理。它常常是注入得手后第一个可观测的症状,而且它出现在任何损害之前。把它连同 trace 一起路由进事故响应,而不是路由进一张看板。
  • 把评测与红队环境加固到同等标准,或更高。它们跑的是能力更强、拒答更少的配置,面对的是刻意对抗的目标。如果你用了第三方评测伙伴,他们那个环境的网络配置如今就是你的风险;2026 年的那几起披露说得很清楚:它需要被验证,而不是被假定——见第三方模型与厂商风险
  • 写下"没有互联网"到底指什么,然后去验证这句话。如果一份文档、一段提示词或一纸合同声称某个环境没有互联网访问,那就必须有人从那个环境内部真的发起过一次连接尝试,并把结果留了下来。在这件事上翻车的两家实验室,都相信了那句话。

第一个小时比路线图更值钱:拿一个智能体,撤掉它的默认路由,把它指向一个代理,然后让它失败。它在头一天里真正打到的那些目的地,就是你真实的允许清单,而它会比任何人预计的都短。然后把厂商凭据挪进代理,让沙箱从不持有它们;按任务 ID 记录每一次尝试;再加一条"首见目的地"告警。算力隔离拦住的是智能体弄坏这台机器;只有出站控制才决定别人家的哪些机器在射程之内——而它正是在提示词注入已经得手之后,仍然管用的那项控制。

相关:沙箱与代码执行讲这是五条隔离决策中的哪一条,沙箱与隔离模式讲具体实现形态,提示词注入讲这项控制所约束的那种攻击,智能体威胁模型讲出站在整幅图里的位置。