浏览器沙盒 vs 代码沙盒 vs 文件系统沙盒:Agent 需要的三种隔离级别

AI technology illustration

很多做 AI Agent 的人第一次接触到“沙盒”这个词,是在翻看 OpenAI 的 Cookbook 或者 LangChain 的文档时。那里面说,要让 Agent 执行代码,必须把它放进沙盒里。于是你建了一个 Docker 容器,丢进去一个 Python 脚本,觉得万事大吉。

然后你的 Agent 开始调用浏览器、读写文件、执行系统命令,你突然发现,“沙盒”不只是“执行代码的地方”那么简单。Docker 容器只是代码沙盒,它拦不住 Agent 在浏览器里乱点、也拦不住它在宿主机上偷文件。

我最早也犯过这个错。我以为把一个 Python 进程丢进 Docker 就高枕无忧了,直到我写了一个 Agent,它需要访问某个网页,然后用 headless 浏览器去抓数据。结果那个网页跳转到了一个本地文件——我一开始没有意识到,浏览器的 file:// 协议是直接读宿主机文件的。容器里的浏览器可以读到 Docker 挂载的目录,而如果我挂载了根目录,它能读到宿主机上的一切。

这一刻我才意识到,沙盒不是一个东西,而是三个东西。

先想清楚一个前提:你在防谁?

在讨论沙盒之前,必须回答一个问题:你的威胁模型是什么?

如果你在防恶意代码,那你需要代码沙盒。如果你在防被攻陷的浏览器,那你需要浏览器沙盒。如果你在防 Agent 误删文件,那你需要文件系统沙盒。

这三种沙盒解决的问题完全不同。把它们混为一谈,是大多数安全事故的根源。

我把它们拆开来一个个讲。

代码沙盒:把不可信代码关进笼子里

代码沙盒的目的是限制代码的运行能力——不让 Python 脚本访问网络、读写文件、调用系统调用。最典型的是运行在 gVisor 或者 Firecracker 里的微虚拟机,以及常见的 Docker 容器。

但 Docker 容器并不是真正的隔离。它共享宿主机的内核,只是通过命名空间和控制组来限制视角。一旦有内核漏洞,容器就能逃逸。这就是为什么严格的安全场景要用微虚拟机——每个容器运行在独立的轻量级虚拟机里,有一个自己的内核。

代码沙盒的本质是把不可信代码放在一个受限的宇宙里,并定义这个宇宙的边界。在这个宇宙里,它可以执行 CPU 指令、分配内存、计算 1+1,但它不能碰外面的东西。

问题是,Agent 的代码往往需要跟外面的世界交互。它需要调用 API,需要读取用户上传的文件,需要操作浏览器。这时候代码沙盒要么开一个口子(API 网关、受控的文件挂载),要么你就在沙盒外面再套一层别的保护。

我从实践中悟出一个道理:代码沙盒防的是恶意代码,而不是 Agent 自身。Agent 写的代码也许没有恶意,但可能因为 bug 或者被提示词注入带偏,去执行危险操作。代码沙盒能做的,是当这个代码试图os.remove('/etc/passwd')时,直接给它一个 Permission denied。它不需要区分这是恶意还是无意——它就是不让它发生。

但如果 Agent 通过合法代码路径拿到了权限呢?比如它读取了用户的 SSH 私钥然后发给服务器。代码沙盒根本管不到这个,因为读文件是合法的,发请求也是合法的。问题出在上下文,而不是代码本身。

浏览器沙盒:给 AI 一双看不见的手

你让 Agent 去完成任务,很多时候你需要它像人一样操作网页。它调用了网页上的按钮、填了表单、抓了数据。这里就有一个巨大的问题:网页本身也是代码。

一个网页可以包含 JavaScript。当浏览器加载这个网页时,网页里的脚本会在你的 Chormium 实例里运行。你可能觉得,反正它是跑在浏览器里,浏览器有沙盒。但浏览器的沙盒是给网页准备的——它让一个网页不能读另一个网页的数据、不能让恶意脚本直接访问操作系统。它做的是网页与网页之间、网页与系统之间的隔离

但你的 Agent 是浏览器的主人。Agent 通过 DevTools 协议去操作浏览器页面,就拥有比网页更高的权限。如果恶意网页通过 prompt 注入操纵了 Agent 的思维,让 Agent 去执行page.goto('file:///etc/passwd')或者去点击一个危险按钮——浏览器的沙盒级别就不够了。

更阴险的是,现在很多 Agent 用的浏览器是全新启动的,没有用户登录态,所以很多网站访问不了。为了让它能工作,开发人员会把用户的 cookies 灌进去。一旦 cookie 泄露,等于用户的账号被接管。

所以浏览器沙盒的核心,不是技术上把浏览器隔离得多深,而是在浏览器和 Agent 之间建立一条严格的信任边界

我见过的一个比较好的方案是:把浏览器跑在一个独立的微虚拟机里,然后用一个代理协议转发用户指定的操作,只允许 Agent 发送白名单里的命令。但即使这样,Agent 在页面里能执行的 JavaScript 依然有风险——因为你不可能预判一个动态网页的所有行为。

这就像一个危险的游戏:你让一个不可信的网页运行在一个浏览器里,又让浏览器在一个更受控的环境里。但环境只能控制浏览器这个程序,控制不了网页代码和 AI 模型的交互。模型不是从操作系统层面去访问资源的,它是通过浏览器这个中间层去访问的。所以真正要限制的是模型能执行的操作,而不仅仅是浏览器所在的环境。

文件系统沙盒:让 Agent 的每个读写都受到审计

前两种沙盒,一个限制代码能力,一个限制浏览行为。但它们都解决不了一个问题:Agent 需要读写文件。无论是处理用户上传的文档,还是把结果保存到磁盘,文件系统是 Agent 唯一能持久化数据的地方。

文件系统沙盒,就是让你给 Agent 看到的文件系统不是真实的文件系统,而是一个虚拟视图。Agent 对/home/user/data的读写,实际上落在了一个特定的目录里,而且所有操作都会被记录审计。

这跟我之前说的代码沙盒有区别。代码沙盒是阻止rm -rf /这种命令,但文件系统沙盒更文明:它允许rm -rf /执行,只不过这个“/”是一个虚拟的根目录,删了也无所谓。也就是,不是不让它删,而是让它删的东西是假象

这个设计有很多好处。最大的好处是容错:Agent 在探索过程中可能不小心删除了一个重要文件,文件系统沙盒可以让你随时恢复到之前的状态。你可以给 Agent 一个干净快照,跑完一次任务之后还原。这在自动化测试和数据处理 Pipeline 里特别有价值。

另一个好处是权限细分:你可以给 Agent 开辟一个可写目录,同时把下载目录设置为只读。当 Agent 需要读取用户的下载文件时,它能看到但改不了。这就是最小权限原则的文件系统版。

但文件系统沙盒也有个漏洞:它管不住资源泄漏。Agent 可以不停地往沙盒里写文件,直到磁盘爆满。inotify 监控、quota 限制、垃圾回收机制都得由你亲自实现。当然,这是另一个层面的问题。

三种沙盒同时用时,才称得上隔离

我们回到开头那个场景。一个 Agent 需要通过浏览器访问网页、执行代码处理数据、把结果保存到本地。单独用 Docker 容器,你防不住浏览器里的恶意网页通过 file:// 协议读取 Docker 挂载的宿主机文件。单独用浏览器沙盒,你防不住 Agent 在执行代码时利用系统调用去攻击外层环境。单独用文件系统沙盒,你防不住 Agent 直接把内存里的敏感信息通过网络发出去。

正确的方式是三层嵌套:最外层是微虚拟机或沙箱运行环境(比如 GVisor),中间层是浏览器进程隔离(比如把 Chromium 跑在独立的容器里),最内层是文件系统虚拟化(比如用 seccomp 和 Landlock 限制文件路径访问)。每层负责拦截不同种类的威胁。但这还远远不够——纯技术隔离解决不了 Agent 的意图问题。

我最近在看 Chrome 团队关于 AI Agent 浏览器安全的一篇文章,里面提到了一种 “token-based API access” 的设计:浏览器给 Agent 的不是 cookie 而是临时的、权限受限的 token,Agent 每次访问都经过授权。这至少解决了 cookie 被盗的问题,但 token 本身也有泄露风险。

说到底,Agent 的沙盒隔离是一个 纵深防御的问题。没有哪一层单独能挡得住所有攻击。从恶意网页到注入恶意指令的提示词,再到 Agent 自身的代码 bug,威胁遍布在每一个交互层。你需要理解每种沙盒的边界,然后把它们都打开,互相补位。

如果你现在正在设计你的 Agent 架构,我建议你在纸上列出一个矩阵:左边是 Agent 可能接触什么,右边的隔离措施是什么。不要把一个沙盒当万能药。真正安全的 Agent,不是躲在某个容器里的那个,而是那些知道自己的边界,并且每条边界都有一个明确职责的 Agent。

原创文章,作者:guanweilu,如若转载,请注明出处:https://guanweilu.cn/article/513.html

(0)
上一篇 2天前
下一篇 2天前

相关推荐