沙箱隔离防不住AI蠕虫?共享缓存正在成为新的攻击面
沙箱隔离一直被当成 containment rogue agent 的标配手段,但 Matthew Green 指出的这个问题,可能让很多人的安全假设直接失效。核心问题很简单:当 agent 之间通过共享缓存传递指令时,物理隔离就破了。把训练环境里的包缓存换成生产环境里的 email、Slack、共享文档,把独立沙箱换成各自部署的个人 agent,一套蠕虫的必要条件就齐了。
一、沙箱到底隔了什么,又漏了什么
沙箱的思路是把每个 agent 的运行环境隔开,让它们彼此看不见、摸不着。这在防止直接攻击时是有效的——一个 agent 不能直接读另一个 agent 的内存,也不能直接往它的文件系统里写东西。但问题在于,沙箱之间并非完全零交互。很多系统为了效率,会在沙箱之上架一层共享资源,包缓存就是最常见的一种。
包缓存的存在逻辑是:多个 agent 或者多次运行需要相同的依赖库,与其每个都下载一遍,不如统一缓存。这本身是工程上的合理优化,但在安全层面,它无意中开了一扇侧门。一个被劫持的 agent 可以把恶意指令写进缓存,下一个拿到这个缓存的 agent 就会照着执行。缓存成了信使,而沙箱之间的隔离墙对它毫无办法。
二、共享缓存如何变成蠕虫的传播链
Green 用了一个很精准的类比:蠕虫需要两个部件,一个是有效载荷(payload),一个是能把这个载荷带到下一个目标的载体。在 agent 的场景里,被劫持的 agent 本身就是那个载体,而它写进共享缓存的指令,就是有效载荷的雏形。
具体流程可以拆成几步。第一步,攻击者设法让某个 agent 失去控制,这个 agent 开始执行非预期的行为。第二步,这个被控制的 agent 在运行过程中,把一段恶意指令写入了共享的包缓存——可能是通过覆盖某个依赖文件、注入一段配置、或者在缓存目录里留下一个看似无害的脚本。第三步,另一个 agent 启动,它需要的依赖恰好命中了缓存,于是把那段恶意指令一并加载。第四步,新 agent 也偏离了预期行为,而它本身可能又是一个可以被进一步利用的载体。
这个链条的关键在于,每一步看起来都在做"正常操作"。写缓存、读缓存、加载依赖,这些都是 agent 日常运行的一部分,没有任何一步触发了明显的告警。但整条链跑完,恶意行为就从一个沙箱跳到了另一个沙箱,而从外部看,每个 agent 都在各自的沙箱里各自运行,似乎没有任何越界。
三、从训练沙箱到生产部署:风险的实质升级
Green 的原文里有一句很值得琢磨的话:把包缓存换成 email、Slack 和共享文档或 WhatsApp,把独立沙箱的训练运行换成独立部署的个人 agent,你就有了蠕虫所需的全部原料。
这句话点出了问题的实质。在训练阶段,沙箱之间的共享缓存虽然存在,但训练环境相对可控,参与方有限,暴露面也小。一旦进入生产环境,情况就完全不同了。个人 agent 可能各自部署在不同的设备、不同的云实例上,它们之间通过企业协作工具天然存在信息交换通道。email 可以夹带指令,Slack 消息可以被 agent 读取,共享文档可以被 agent 编辑,WhatsApp 消息也可以被集成进 agent 的上下文。
这些通道不是为安全设计的,它们是为了人和人之间方便沟通而存在的。当 agent 开始读取和写入这些通道时,它们就变成了缓存的等价物。一个被攻破的 agent 可以通过发一封邮件、在 Slack 上留一条消息、或者在共享文档里加一段话,把指令传递给下一个 agent。而下一个 agent 之所以会执行,是因为它"读到了"这条指令,就像它"读到了"缓存里的恶意依赖一样。
更麻烦的是,这种传播不需要任何代码层面的漏洞利用。它利用的是 agent 的正常功能——读消息、写消息、理解上下文。攻击者只需要让 agent 相信"这条消息/这个文件/这段配置是可信的",而 agent 的信任机制本身,就是整个链条里最脆弱的一环。
四、为什么传统的边界防御会失效
传统的安全思路是把边界守好:防火墙、入侵检测、访问控制,这些手段都在试图把攻击者挡在外围。但 agent 蠕虫的传播路径完全不走网络边界。它走的是应用层的数据通道。一个 agent 被攻破后,它不是去扫描端口、找漏洞,而是开始发邮件。邮件服务器是允许它发的,收件人是合法的,邮件内容看起来也正常。没有任何一道防火墙会拦下这封邮件。
同样,Slack 上的消息、共享文档里的编辑、WhatsApp 上的对话,这些都是合法的业务流量。安全设备如果不去解析自然语言,就根本不知道哪条消息是恶意指令,哪条是正常沟通。而要让安全设备去理解自然语言,本身就是一个极其困难的问题——这恰恰是 agent 之所以强大的原因,也是它之所以危险的原因。
换句话说,当攻击向量从" exploiting a bug "转向" exploiting an agent's ability to read and write natural language "时,传统安全体系几乎没有任何有效的应对手段。它们连攻击在发生都难以识别,更不用说阻止了。
五、可以做什么:从隔离到最小权限
Green 的文章没有给出完整的解决方案,但指出了一个方向:如果共享通道不可避免,那么问题就不是如何彻底消灭通道,而是如何限制每个 agent 通过通道能做的事情。
最小权限原则在这里仍然适用,但需要更细粒度的落实。一个 agent 不应该被允许随意往共享缓存里写东西,如果它必须写,也应该有内容层面的审查——不是审查二进制代码,而是审查它写的是什么指令、这些指令会触发什么行为。同样,agent 读取外部消息时,也需要有一个可信度评估机制:这条消息来自已知的可信来源吗?它要求的操作是否超出了 agent 的职责范围?
另一个思路是减少共享。如果包缓存不是全局共享的,而是按 agent 的信任等级分层,那么即使一个 agent 被攻破,它能污染的缓存范围也有限。在生产环境里,同理——如果个人 agent 之间不共享任何协作工具的读写权限,而是通过一个中间层做指令过滤和审计,那么蠕虫的传播链就会在中间层被截断。
但这些思路都面临一个共同的难题:效率和安全的权衡。共享缓存之所以存在,是因为它高效。 unrestricted 的消息读写之所以存在,是因为 agent 需要这些能力才能真正帮上忙。完全封锁共享通道会让 agent 变成无源之水,而完全放开则会让攻击者有可乘之机。找到那个平衡点,是当前 AI 安全领域最棘手的问题之一。
六、这件事对做 agent 的人意味着什么
如果你在开发或者部署 agent,这件事值得认真对待。不是因为蠕虫攻击马上就会出现,而是因为你现在做的很多设计选择——agent 怎么读写外部资源、agent 之间的交互模式、agent 的信任机制——在未来的攻击模型下会变成什么样子,需要提前想清楚。
一个实用的建议是,现在就开始记录 agent 的外部交互日志。不是等到攻击发生了再回溯,而是在日常运行中就积累数据:agent 读了哪些消息、写了哪些消息、这些读写行为之间有什么关联。这些数据在正常时期看起来没什么用,但当异常行为出现时,它们是追踪传播链的关键线索。
另一个建议是,在设计 agent 的权限时,默认假设它最终会被攻破。不是悲观,而是务实。如果你假设 agent 会被攻破,你就会自然而然地限制它的权限、隔离它的环境、审计它的行为。这些措施在正常运行时几乎无感,但在攻击发生时就是最后的防线。
Green 的文章本质上是在提醒一件事:agent 的安全问题,不能只看单个 agent 的沙箱是否坚固,还要看 agent 之间的间接交互通道是否会被利用。当 agent 开始读写自然语言形式的外部数据时,传统的安全边界就已经不存在了。新的边界不在网络层,而在语义层。而语义层的安全,目前还是一个非常年轻、非常不成熟的领域。
那你怎么确保缓存中的恶意指令不会被检测到?