Cloudflare Sandbox:在 Workers 边缘运行不可信代码的两种隔离环境
阅读时间: 大约 18 分钟
Cloudflare Sandbox:在 Workers 边缘运行不可信代码的两种隔离环境

当应用需要执行”自己没有写过、也不能信任”的代码——例如大模型当场生成的脚本、用户上传的应用、仓库里拉下来跑一遍的测试套件——直接放进主进程是安全大忌:一段恶意生成的代码可以读取应用内存、偷走凭证、对内网发起连接。Cloudflare 在其 Workers 平台上提供的 Sandbox,就是专门承接这类负载的隔离执行环境。官方定位很明确:“A sandbox is an isolated place to run code. The code cannot directly read the memory or data of your application.”(沙盒是一个隔离的代码运行处,代码无法直接读取应用的内存或数据)。本文基于 Cloudflare 官方文档,解析它提供的两套环境、资源规格、计费方式,以及官方自己标注的 Beta 边界与坑。
一、背景动机:为什么不能在 Worker 主进程里跑不可信代码
Workers 是 Cloudflare 的无服务器运行时,单进程内同时服务大量租户的代码。把模型生成的代码或用户上传的程序直接 eval 进主进程,意味着这段代码天然继承 Worker 的 bindings、环境变量、凭证与对外网络能力,一旦内容被污染,危害与主进程自身等同。传统解法是另起一台虚拟机或一个 Docker 集群来跑代码,代价是冷启动慢、要自己管编排与扩缩容;而 Cloudflare Sandbox 的思路是把”隔离执行”做成 Workers 的内建能力,由已经承载流量的 Worker 决定把什么方法、什么数据、什么网络权限交给沙盒。
官方列出的典型用途包括:Agent 写入并执行的代码(coding agent runner)、AI 代码解释器(code interpreter)、用户上传应用、数据分析、开发预览(development preview)、构建流水线,以及任何”不应与应用共享进程”的工作。需要注意的是,Sandbox 面向 Workers Paid 付费套餐(每月 5 美元起),免费套餐不可用。
二、概念定位:两套环境,而不是一个沙盒
Cloudflare 明确提供两种互不等价的沙盒环境,同一个应用可以同时使用两者:
- Container(容器):运行你提供的 Linux 镜像,实例是一个完整 Linux 环境,可以跑任意语言、常驻后台进程、监听端口。每个实例是一台 Firecracker microVM,拥有独立内核与独立网络,没有其他负载与其共享;互联网 HTTP 请求只能经由你的 Worker 才能到达实例。
- Dynamic Worker(动态 Worker):运行在运行时里按需新建的一个 Worker,不可信代码可以是 JavaScript、Python 或 WebAssembly(TypeScript 需先编译为 JS)。它由 V8 引擎在自身内存隔离的沙盒中运行,Workers 运行时在其外再包一层进程级沙盒。它只能访问你的 Worker 主动传入的模块、方法与值,不继承 Worker 的 bindings、凭证与数据。
三、技术机制:Worker 做”网关”,隔离体做”执行”
3.1 Container:Durable Object 拉起的 microVM
在当前(1.0)文档中,Container 通过 Worker 里的一个 Durable Object 调度策略启动实例:
container.start({
image: "cloudflare/debian-trixie",
entrypoint: ["sleep", "infinity"],
enableInternet: false,
});
const process = await container.exec(["uname", "-a"]);
const output = await process.output();官方示例镜像 cloudflare/debian-trixie 预装 Node.js 24。Worker 决定容器何时启动、能否访问公网、哪些 HTTP 请求能到达它。官方强调:Container 默认 enableInternet: false,不能触网,只有 Worker 里的出站处理器(outbound handler)拦截放行的请求才能离开;若为了 npm install 打开公网,依赖本身也可能把读到的 token 发往任意服务器,因此官方建议保持默认关网、用出站处理器只放行包仓库等白名单目的地。
3.2 Dynamic Worker:Loader 加载,方法白名单
Dynamic Worker 通过 Worker Loader 的 load() 在运行时加载不可信模块:
const sandbox = env.LOADER.load({
compatibilityDate: "2026-10-21",
mainModule: "code.js",
modules: { "code.js": untrustedModule },
globalOutbound: null,
limits: { cpuMs: 50 },
});
const entrypoint = sandbox.getEntrypoint("Code");
const result = await entrypoint.evaluate();其中 globalOutbound: null 会让不可信代码里的 fetch() 与 connect() 直接抛错,阻断直接联网;limits.cpuMs 可限制其 CPU 时间(示例给的是 50 毫秒)。官方还提供 get() 配合稳定 ID 复用一个”预热”的 Dynamic Worker。需要说明:Dynamic Worker 不能启动子进程、不能加载原生插件,Workers 对部分 Node.js 模块只是部分兼容,所以只要代码依赖操作系统或原生工具链,就必须选 Container。
3.3 选型逻辑与组合用法
官方给出的判据很直接:代码”期望一个 Linux”(要跑 npm test、常驻预览服务器、编译器工具链、原生二进制)用 Container;代码”只调用你定义好的已知方法”(比如对已知 API 做过滤处理)用 Dynamic Worker。官方还举了组合场景:coding agent 通过 Dynamic Worker 里收窄的方法回答仓库问题,当 agent 提出补丁时,同一个 Worker 再启动一个 Container 跑仓库的测试套件。
四、关键规格与计费(来自 Containers 平台,官方口径)
Sandbox 的 Container 构建在 Cloudflare Containers 平台之上,因此资源规格与定价直接沿用该平台。官方说明:内存与磁盘按你选择的实例规格(provisioned)计费,CPU 按实际活跃用量计费,容器每活跃 10ms 计一次费,进入休眠后停止计费。
4.1 实例类型(官方默认值,均可申请提升)
| 实例类型 | vCPU | 内存 | 磁盘 |
|---|---|---|---|
| lite | 1/16 | 256 MiB | 2 GB |
| basic | 1/4 | 1 GiB | 4 GB |
| standard-1 | 1/2 | 4 GiB | 8 GB |
| standard-2 | 1 | 6 GiB | 12 GB |
| standard-3 | 2 | 8 GiB | 16 GB |
| standard-4 | 4 | 12 GiB | 20 GB |
自定义实例类型约束:vCPU 1–4、内存最高 12 GiB、磁盘最高 20 GB,且每 vCPU 至少配 3 GiB 内存;低于 1 vCPU 的负载请直接用预置规格。账户级默认上限:并发内存 6 TiB、并发 vCPU 1500、并发磁盘 30 TB,单账户镜像总存储 50 GB;快照最大 20 GB,自创建或最近一次恢复起保留 30 天。
4.2 计费(Workers Paid 套餐内含额度)
| 资源 | 每月包含(Workers Paid) | 超额单价 |
|---|---|---|
| 内存 | 25 GiB·小时 | $0.0000025 / GiB·秒 |
| CPU | 375 vCPU·分钟 | $0.000020 / vCPU·秒 |
| 磁盘 | 200 GB·小时 | $0.00000007 / GB·秒 |
网络出口:北美与欧洲 $0.025/GB(每月含 1 TB),大洋洲、韩国、台湾 $0.05/GB(含 500 GB),其他地区 $0.04/GB(含 500 GB)。此外还会产生 Workers 请求费、每个实例对应的 Durable Object 费用,以及可选的 Workers Logs 观测费。
4.3 请求子请求限制
SDK 0.x 的 HTTP 传输模式下,每次 exec()/readFile()/writeFile() 都计为一个 Workers 子请求:Workers Free 每请求 50 个、Workers Paid 每请求 1000 个。高频操作应启用 RPC 传输(SANDBOX_TRANSPORT=rpc)把所有 SDK 调用复用到一条持久连接上,只占一个子请求;官方明确 WebSocket 传输已废弃,新应用用 RPC。
五、评测口径与官方承认的边界(nuance)
读官方材料时必须注意以下口径偏差,不宜当成”开箱即用的成熟产品”宣传:
- Container 的 Durable Object 调度策略处于 public beta:官方在概述页直接标注,未来行为可能调整;本文所列规格来自底层 Containers 平台,Sandbox 本身并未发布独立的性能基准。
- 官方未公布冷启动时延数字:文档只说明”容器必须先启动,第一条命令才能运行”(Container cold starts),没有给出 microVM 拉起的毫秒级数据,边缘低延迟的说法属于平台定位而非已公布的实测。
- 内存/磁盘按规格而非实际占用计费:哪怕容器空载,你选了 standard-2 的 6 GiB 内存也按 6 GiB 计;只有 CPU 按活跃量计,成本估算时容易误判。
- 内部运行中的任务也可能被超时杀掉:实例内部代码的活动不计为活动信号,无请求到达时,闲置超时一到,即使构建、测试套件或 agent 任务仍在跑,实例也会停止;需要用每分钟触发的 Durable Object alarm 保活。
destroy()立即停止、不给进程清理时间,超时停止则先发 SIGTERM。 - 本地开发依赖 Docker:
wrangler dev在本机用 Docker 跑实例,cloudflare/debian-trixie需要 Wrangler 4.141.0 或更高版本;这不是纯云端模拟。 - 快照与命名会带来数据串号风险:快照会把文件(包括
.git/config里的 token)一并保存,用同名恢复会带回旧文件;官方明确”沙盒名只决定命中哪个实例,不证明谁发的请求”,必须在 Worker 侧鉴调用方身份并由身份派生沙盒名,否则复用名字会把上一个用户的文件交给别人。 - 输出同样不可信:沙盒返回的结果、文件、HTTP 响应都来自不可信代码,落库或展示前要像用户输入一样校验;网页预览还会把内容渲染到访客浏览器,XSS 面更大。
六、适用与不适用场景
适合:
- 跑 LLM/Agent 当场生成代码的 AI 应用(代码解释器、agent runner),安全隔离优先;
- 多租户用户上传应用、在线数据分析、开发预览,需要按请求拉起、用完即停的突发负载;
- 已有 Node.js/Python 工具链、希望少改代码就获得 Linux 执行环境的团队;
- 只需对已知方法做沙盒计算、且想复用预热实例的轻量场景(Dynamic Worker)。
不适合:
- 需要稳定常驻、长连接、强状态的后端服务——沙盒为突发、短任务设计,受闲置超时与生命周期约束;
- 需要原生系统服务、特权操作或 Docker-in-Docker 重负载的场景(镜像与规格有上限,且 DinD 属于指南级能力而非一等公民);
- 对冷启动时延有硬性毫秒级要求且官方未给出承诺的关键路径;
- Workers Free 用户(套餐门槛即排除)。
七、客观分析:优势与局限
优势:
- 双层隔离覆盖两类负载:microVM(独立内核/网络)与 V8 isolate(内存隔离)分别对应”要 Linux”和”只调方法”两种需求,且同一应用可混用;
- 凭证天然留在 Worker:沙盒不继承 bindings,默认断网,安全模型把”信任边界”显式交到应用代码手里;
- 按用量计费、可缩容到零:10ms 粒度计费、休眠即停,突发型代码执行成本可控;
- 与 Workers/Durable Objects/R2 生态原生打通:可挂载 R2 bucket、用快照保存工作区、经 Worker 暴露预览 URL,无需另建编排层。
局限:
- 成熟度仍在 Beta:Container 的 DO 调度策略为公测,SDK 正从 0.x 向 1.0 迁移,旧 SDK 文档与新文档并存,迁移成本需单独评估;
- 缺乏官方性能基准:冷启动、吞吐、并发上限都没有对外实测数字,容量规划要自行压测;
- 生命周期管理负担转移给开发者:保活、超时、快照、命名鉴名都要自己实现,官方只给机制不给托管;
- 生态相对年轻:对比 E2B、Firecracker 托管代码解释器等专门方案,其周边工具链与社区案例仍在积累。
八、它意味着什么
Sandbox 的价值不在于”又一个容器产品”,而在于把不可信代码的执行环境内嵌进边缘 Serverless 运行时:Agent 写代码、用户跑脚本这类天然突发、天然多租户、天然不可信的负载,不必再外挂一套虚拟机平台。对已经在 Workers 上构建 AI 应用的团队,它显著降低了”安全执行生成代码”的工程门槛;但由于关键性能数字未公开、核心调度仍在公测,生产采用前应自行压测冷启动与并发,并把生命周期与鉴名逻辑补齐。