Vercel 指出运行 AI Agent 仅靠 microVM 计算隔离远远不够——代码仍可通过网络外泄数据、攻击内部服务或滥用环境凭证;完整沙箱需同时控制出口网络和权限生命周期。
运行不受信任的代码,光把它和宿主机隔离开是不够的。你还需要控制它能访问哪些东西。
这一点随着 AI Agent 的能力增长变得越来越重要——它们可以读取文件、执行命令、安装依赖包、生成程序。MicroVM 能防止这些代码访问宿主机或其他工作负载。但仅凭自身,它无法阻止代码外传数据、探测内部服务、攻击互联网上其他系统,或者使用环境中已有的凭据。
没有出口控制的网络隔离,困住的只是进程,而不是它的后果。
一个完整的沙箱因此需要同时具备计算隔离和对网络层面权限的控制:代码能连接到哪里、能使用哪些凭据、以及这些权限在工作负载生命周期中如何变化。这些控制是安全边界的一部分,不是事后才需要补上的东西。
计算隔离回答了一个重要问题:这个程序在它运行的机器上能访问什么?网络隔离则回答了另一个问题:它能通过网络访问或攻击什么?
考虑一个能读取代码仓库并运行生成代码的 Agent。藏在 issue、日志条目、依赖或源文件里的提示词注入,可能会指示它上传私密数据。生成的程序不需要逃出它的 microVM。只要出站流量不受限,它就能把读到的任何内容发送给外部服务器。
同样的访问能力可以被用来扫描内部网络、外传数据和凭据,或者调用经过认证的 API。从攻击者的角度看,穿越 VM 边界也许根本没有必要。没有网络边界,沙箱只能算半个沙箱。
最近的安全研究已经清楚地揭示了一种模式:不受信任的代码不需要穿越 VM 边界就能逃出控制。它只需要一条安全模型没有考虑到网络路径。
这条路径可能是一个在看似断网环境中仍可用的 DNS 解析器、一个空白的白名单导致默认放行、一个策略引擎和代理对主机名解析方式不一致、或者一个可信的包服务被当作中继。它们中的任何一个都可以给不受信任的代码提供外传数据、接收指令、或向更敏感基础设施渗透的通道。
这些不是计算层面的逃逸。内核、VM 或容器边界可能继续完全按设计运行,而控制却已经失效。实际的安全边界还包括 DNS、代理、身份服务、内网以及每一个被明确允许的目的地。
无论我们把结果称为逃逸还是绕过,要求都是一样的:沙箱必须考虑到不受信任代码可以通信的每一条路径。
完全断开沙箱的网络确实关闭了网络路径,但也让许多工作负载变得不切实际。一个 Agent 可能需要克隆仓库、安装依赖、调用 AI 模型、查询数据库或上传结果。
不受限制的互联网访问不是唯一的替代方案。一个有用的沙箱应该只授予工作负载所需的连接能力:
允许一个 AI Provider,但不允许其他公共目的地。
允许一个 AI Provider,但不允许其他公共目的地。
只允许一个对象存储桶,而不是整个云网络。
只允许一个对象存储桶,而不是整个云网络。
可以访问一个私有服务,同时屏蔽其余私有地址段。
可以访问一个私有服务,同时屏蔽其余私有地址段。
在可信的初始化阶段安装依赖,然后在生成代码运行前移除注册表访问权限。
在可信的初始化阶段安装依赖,然后在生成代码运行前移除注册表访问权限。
对 API 请求进行身份验证,但不将 API Key 放入沙箱内部。
对 API 请求进行身份验证,但不将 API Key 放入沙箱内部。
一个实用的策略模型应该支持完全开放访问、完全网络隔离以及细粒度策略——默认拒绝所有未匹配的流量。这些策略应该能够将域名规则与允许和拒绝的地址范围组合使用。
域名和 CIDR 解决的是不同的问题。域名策略对于现代服务足够精确——它们的 IP 地址会变化,或者托管许多不相关的主机名。CIDR 策略则跨协议工作,并能对固定基础设施和私有网络实施控制。
连接能力也应该是临时的。一个工作流可以在开始时访问包仓库、在不可信代码执行前收窄权限、短暂允许一个输出目的地、最后完全停止出站访问——整个过程不需要重启工作负载。
Vercel 沙箱防火墙运行在宿主机上,在 microVM 外部,沙箱内的代码无法修改或禁用它。
Linux 网络透明地重定向出站 TCP 连接和 DNS 查询经过防火墙。工作负载不需要配置代理,而且防火墙保留了每个连接的原始目标地址。
对于域名受限的连接,防火墙检查 TLS 握手的开始部分并提取 Server Name Indication(SNI)。这样在防火墙向上游建立连接之前就能识别出请求的主机名。它会用沙箱的域名策略检查该主机名,同时用 CIDR 策略检查目标地址。
正常的允许的 TLS 连接直接通过,无需解密。防火墙只读取执行策略所需的未加密握手信息,然后将工作负载直接连接到其目标地址。
有些策略需要检查或修改 HTTP 请求本身。对于这些配置了相应功能的域名,Header 注入和请求转发会选择性地使用属于该沙箱的证书机构终止 TLS。然后防火墙可以按主机名、路径、方法、查询参数或请求头匹配请求,再注入凭据或将有问题的请求转发到你信任的端点。
DNS 流量使用相同的域名策略进行过滤。这些层共同控制了沙箱能连接到哪里以及特定连接获得什么权限。
一个 Agent 通常需要调用需要认证的服务,但如果凭据存储在环境变量或文件中,它就是一种可转移的持有者凭证。沙箱内的每个程序都能读取它。恶意代码可以把它复制到第三方服务,在沙箱停止后很长时间内仍能保留和使用。
Vercel 沙箱选择在宿主机网络边界注入凭据。防火墙为沙箱即时创建一个专属的证书机构,并将其添加到沙箱的信任证书列表中。对于配置的目的地,它选择性地终止 TLS、添加或替换认证头,然后与上游服务建立新的 TLS 连接。
凭据永远不会进入 microVM。它也永远不会以未加密状态离开宿主机。证书机构是该沙箱专用的,在沙箱停止时被销毁。
注入还限制了凭据可以被使用的范围。防火墙只将凭据发送到其配置的目的地,因此上传沙箱的文件或环境到其他服务不会转移该权限。匹配器可以按路径、方法、查询或请求头进一步限制。生成代码可能获得向一个端点提交结果的权限,而无需获得从同一 API 读取其他资源的权限。
注入的凭据仍应遵循最小权限原则,访问权限仅限于工作负载所需的特定操作。
静态白名单无法表达所有的安全需求。有些工作负载需要检查载荷、执行业务规则、记录审计日志、或使用只有你自己拥有的上下文做出授权决策。
请求转发将选定的 HTTPS 请求通过你控制的代理发送。代理收到原始请求以及 Vercel 发行的 OIDC 令牌,其中标识了发起该请求的团队、项目和沙箱。
这个代理成为一个可编程的策略层。它可以:
在请求离开环境前清除敏感字段。
在请求离开环境前清除敏感字段。
执行按用户或按沙箱的授权。
执行按用户或按沙箱的授权。
将包下载路由到供应链扫描,并对违反组织策略的制品进行阻断。
将包下载路由到供应链扫描,并对违反组织策略的制品进行阻断。
记录请求和响应以满足合规工作流。
记录请求和响应以满足合规工作流。
用沙箱身份换取一个范围收窄的凭据。
用沙箱身份换取一个范围收窄的凭据。
拒绝不符合组织策略的操作。
拒绝不符合组织策略的操作。
策略及其密钥位于沙箱外部,即使被治理的代码在 microVM 内部拥有完整的 root 访问权限。你可以在沙箱文档中阅读更多关于请求代理的内容。
我们相信,一个沙箱不应该在它能安全处理不受信任代码之前就要求用户提供支付方式。
每个 Vercel 沙箱都同时包含计算隔离和完整的防火墙,用于定义其中代码可以与哪些对象通信。
目标不是让每个沙箱都完全离线。而是让权限变得明确:
这个沙箱能访问哪些目的地?
这个沙箱能访问哪些目的地?
哪些私有地址段不可访问?
哪些私有地址段不可访问?
哪些请求可以使用凭据?
哪些请求可以使用凭据?
哪些操作必须经过额外的策略检查?
哪些操作必须经过额外的策略检查?
什么时候应该停止所有通信?
什么时候应该停止所有通信?
这些是执行环境的基本属性,不是工作负载进入生产环境后才需要添加的安全控制。我们对此深信不疑,因此将完整的出口防火墙能力开放给所有沙箱使用。
以下策略允许出站连接到某个 API,并只为一项操作注入凭据。其他目的地默认拒绝,且凭据保留在沙箱外部。
1import { Sandbox } from '@vercel/sandbox';2const sandbox = await Sandbox.create({3 networkPolicy: {4 allow: {5 "ai-gateway.vercel.sh": [{6 transform: [{7 headers: {8 "Authorization": `Bearer ${process.env.AI_GATEWAY_TOKEN}`9 }10 }],11 }]12 }13 }14});
同样的策略可以在沙箱运行期间替换:
1await sandbox.update({ networkPolicy: 'deny-all' });
沙箱的定义不仅仅在于代码在哪里运行。更在于它能访问什么、获得什么权限、以及当代码本身具有恶意时哪些边界仍然有效。
网络是沙箱的一部分。
Vercel Sandbox firewall documentation
Vercel Sandbox firewall documentation
Understanding Vercel Sandboxes
Understanding Vercel Sandboxes
Security boundaries in agentic architectures
Security boundaries in agentic architectures