没有网络边界的沙箱,只能算半个沙箱

Vercel Blog 2026-08-13T01:14:48.355114

安全地运行不受信任的代码,不能只把它和宿主机隔离开,还得控制这些代码能访问到什么。随着 AI 智能体(agent)逐渐能够读取文件、执行命令、安装软件包,甚至自己生成程序,这一点变得越来越关键。微虚拟机(microVM)可以阻止代码访问宿主机或其他工作负载,但单靠它,无法阻止代码窃取数据、探测内部服务、攻击互联网上的其他系统,或者滥用环境里的凭据。如果只有隔离、没有出站流量控制,你只是关住了进程本身,却控制不了它造成的后果。因此,一个完整的沙箱既需要计算隔离,也需要对网络授权做控制:代码能连到哪里、能使用哪些凭据,以及这些权限在工作负载的整个生命周期里如何变化。这些控制本身就是安全边界的一部分,而不是事后补装的防护。

沙箱的边界不止一道

计算隔离回答一个重要问题:这段程序在它运行的机器上能访问什么?网络隔离回答另一个问题:它通过网络能访问或攻击什么?比如,有一个智能体读取代码仓库并运行生成的代码。如果某个 issue、日志条目、依赖项或源文件里藏了提示注入(prompt injection),就可能诱导它把私有数据上传出去。生成的程序根本不需要逃出微虚拟机,只要出站流量不受限制,它就能把能读到的一切直接发送到外部服务器。同样的网络访问权限,还可以用来扫描内网、窃取数据和凭据,或者调用需要认证的 API。在攻击者眼里,根本没有必要跨过虚拟机边界——没有网络边界,这个沙箱只能算半个沙箱。

网络绕过同样等于沙箱逃逸

近期的安全研究反复说明一个规律:不受信任的代码要突破隔离,并不一定非得跨过 VM 边界。它只需要一条安全模型没有考虑到的网络路径就够了。

那条路径可能是在一个原本断网的环境中仍可用的 DNS 解析器、一个为空且默认放行的允许列表、一个被策略引擎和代理解读不一致的主机名,或者一个变成转发中继的可信软件包服务。其中任何一个都可能为不可信代码提供通道,用来窃取数据、接收指令或向更敏感的基础设施移动。这些都不是计算逃逸。内核、虚拟机或容器的边界可能依然严格按设计工作,但隔离仍然失效。真正的安全边界还包括 DNS、代理、身份服务、内部网络,以及所有被有意放行的目标。无论我们把结果称为逃逸还是绕过,要求都相同:沙箱必须覆盖不可信代码能够通信的每一条路径。

有用的沙箱需要选择性连接

完全断开沙箱的网络连接能关闭网络路径,但也会让很多工作负载变得不切实际。代理可能需要克隆仓库、安装依赖、调用 AI 模型、查询数据库或上传结果。无限制的互联网访问并非唯一的选择。有用的沙箱应当只授予工作负载所需的连接能力:允许访问某个 AI 提供商,但禁止访问其他任何公共目标;允许访问某一个对象存储桶,而不是整个云网络;允许访问某个私有服务,同时屏蔽其余私有地址空间;在可信的初始化阶段安装依赖,然后在生成的代码运行前移除软件仓库的访问权限;在不把 API 密钥放进沙箱的前提下完成 API 请求的认证。一个实用的策略模型应当支持完全开放访问、完全网络隔离,以及默认拒绝未匹配流量的细粒度策略。这些策略应当能把域名规则与允许/拒绝的地址范围组合在一起。域名和 CIDR 解决的是不同问题。对于 IP 地址频繁变动或托管大量无关主机名的现代服务,域名策略足够精确;CIDR 策略则跨协议生效,适合对固定基础设施和私有网络进行控制。连接还应当是临时的。

一个工作流可以这样配置:开始时允许访问包注册表,在执行不可信代码前收窄权限,临时放行某一个输出目的地,最后彻底关闭出站连接——全程无需重启工作负载。

Vercel 如何执行网络边界

Vercel Sandbox 的防火墙运行在宿主机上,位于 microVM 之外,沙箱内的代码无法修改或关闭它。Linux 网络层会把出站 TCP 连接和 DNS 查询透明地重定向到防火墙,工作负载无需配置代理,防火墙也会保留每个连接的原始目的地址。

对于按域名限制的连接,防火墙会检查 TLS 握手开头,提取出服务器名称指示(SNI)。这样在防火墙建立上游连接之前,就能识别出请求的主机名。接下来,防火墙会把这个主机名与沙箱的域名策略做比对,同时还会把目的地址与 CIDR 策略做比对。

普通允许的 TLS 连接会直接通过,不会被解密。防火墙只读取执行策略所必需的未加密握手信息,然后把工作负载直接连到目的地址。

有些策略需要检查或修改 HTTP 请求本身。仅针对这一类配置过的域名,防火墙会使用沙箱专属的证书颁发机构(CA)来有选择地终止 TLS,以便注入请求头或转发请求。这样一来,防火墙就能先按主机名、路径、方法、查询参数或请求头来匹配请求,然后再注入凭据,或把请求转发给你信任的端点。

DNS 流量也采用同样的域名策略进行过滤。这些层面共同决定了沙箱能连接到哪些地方,以及某个连接能获得什么样的权限。

把凭据放在不可信计算之外

智能体(agent)经常需要调用需要身份验证的服务,但存储在环境变量或文件中的凭据,本质上是一种可转移的持有者凭据——沙箱里的每个程序都能读到它。恶意代码可以把它复制到第三方服务,沙箱停止之后,它仍然能被长期留存和滥用。

Vercel Sandbox 的做法,是在宿主机网络边界处注入凭据,而不是把凭据放进沙箱环境里。

防火墙会为沙箱即时创建一个专用的证书颁发机构,并将其加入沙箱的受信证书。对于配置好的目标地址,它会选择性地终止 TLS、添加或替换身份验证标头,然后与上游服务建立新的 TLS 连接。凭据永远不会进入 microVM,也永远不会以未加密形式离开宿主机。证书颁发机构专属于沙箱,并在沙箱停止时销毁。注入机制还能限制凭据的使用范围:防火墙只会将其发送到配置的目标地址,因此即使把沙箱的文件或环境上传到其他服务,也不会转移该权限。匹配器还可以按路径、方法、查询参数或请求头进一步限制。生成的代码可能被允许将结果提交到某个端点,但无权读取同一 API 的其他资源。注入的凭据仍应遵循最小权限原则,仅授予工作负载所需的具体操作权限。

将你自己的策略放入路径中

静态允许列表无法表达所有安全需求。某些工作负载需要检查负载内容、执行业务规则、记录审计日志,或利用只有你掌握的上下文做出授权决定。请求转发会将选定的 HTTPS 请求发送到由你控制的代理。代理会收到原始请求,以及一个由 Vercel 签发的 OIDC 令牌,该令牌标识发起请求的团队、项目和沙箱。这样一来,代理就变成了可编程的策略层。它可以:

策略及其机密信息始终保留在沙箱之外,即使所治理的代码在 microVM 内拥有完全的 root 权限。你可以在 Sandbox 文档中进一步了解请求代理。

安全应内置于基础产品

我们认为,一个沙箱不应该在还没绑定支付方式之前,就无法安全地处理不可信代码。每个 Vercel Sandbox 都同时提供计算隔离和完整防火墙,用来定义沙箱内的代码可以和哪些目标通信。目标不是让每个沙箱都完全离线,而是让权限变得明确:这个沙箱能访问哪些目标?哪些私网网段不可达?哪些请求可以使用凭据?哪些操作必须经由额外策略把关?通信在什么时候必须全部停止?

这些是执行环境的基本属性,而不是等负载上了生产环境再补的安全控制。我们对此确信不疑,所以将完整的出站防火墙能力开放给了所有沙箱。

快速开始

以下策略只允许出站连接一个 API,并且只为某个操作注入凭据。默认情况下,其他目标一律拒绝,凭据也被保存在沙箱外部。同一份策略可以在沙箱运行期间随时替换。

沙箱的定义不只看代码在哪里运行,还取决于这些代码能触达什么、得到什么授权,以及当代码本身怀有恶意时,哪些边界依然成立。网络本身就是沙箱的一部分。

延伸阅读

查看原文