Sandbox SDK 基于 Containers 构建,每个 sandbox 在自己的 VM 中运行,以实现强隔离。
每个 sandbox 在单独的 VM 中运行,提供完整隔离:
- 文件系统隔离 - Sandbox 无法访问其他 sandbox 的文件
- 进程隔离 - 一个 sandbox 中的进程无法查看或影响其他 sandbox
- 网络隔离 - Sandbox 具有单独的网络栈
- 资源限制 - 按 sandbox 强制执行 CPU、内存和磁盘配额
有关底层容器平台的完整安全详情,请参阅 Containers 架构。
单个 sandbox 内的所有代码共享资源:
- 文件系统 - 所有进程看到相同的文件
- 进程 - 所有 session 可以看到所有进程
- 网络 - 进程可以通过 localhost 通信
要完全隔离,请为每个用户使用单独的 sandbox:
// Good - Each user in separate sandbox
const userSandbox = getSandbox(env.Sandbox, `user-${userId}`);
// Bad - Users sharing one sandbox
const shared = getSandbox(env.Sandbox, 'shared');
// Users can read each other's files!在命令中使用用户输入之前,始终进行验证:
// Dangerous - user input directly in command
const filename = userInput;
await sandbox.exec(`cat ${filename}`);
// User could input: "file.txt; rm -rf /"
// Safe - validate input
const filename = userInput.replace(/[^a-zA-Z0-9._-]/g, '');
await sandbox.exec(`cat ${filename}`);
// Better - use file API
await sandbox.writeFile('/tmp/input', userInput);
await sandbox.exec('cat /tmp/input');Sandbox ID 提供基本访问控制,但不是密码学安全的。请添加应用级认证:
export default {
async fetch(request: Request, env: Env): Promise<Response> {
const userId = await authenticate(request);
if (!userId) {
return new Response('Unauthorized', { status: 401 });
}
// User can only access their sandbox
const sandbox = getSandbox(env.Sandbox, userId);
return Response.json({ authorized: true });
}
};Preview URL 包含随机生成的令牌。任何拥有该 URL 的人都可以访问该服务。
要撤销访问,取消暴露端口:
await sandbox.unexposePort(8080);Quick tunnel(sandbox.tunnels.get(port))返回由 Cloudflare 分配随机主机名的 *.trycloudflare.com URL——没有单独的访问令牌。主机名本身就是访问控制:任何知道 URL 的人都可以访问该服务。要撤销访问,销毁 tunnel:
await sandbox.tunnels.destroy(8080);URL 不会在容器重启后保留,因此重启实际上会轮换主机名。与 preview URL 一样,对任何敏感服务添加应用级认证。详情请参阅 Tunnels API。
from flask import Flask, request, abort
import os
app = Flask(__name__)
def check_auth():
token = request.headers.get('Authorization')
if token != f"Bearer {os.environ['AUTH_TOKEN']}":
abort(401)
@app.route('/api/data')
def get_data():
check_auth()
return {'data': 'protected'}使用环境变量,而不是硬编码密钥:
// Bad - hardcoded in file
await sandbox.writeFile('/workspace/config.js', `
const API_KEY = 'sk_live_abc123';
`);
// Good - use environment variables
await sandbox.startProcess('node app.js', {
env: {
API_KEY: env.API_KEY, // From Worker environment binding
}
});清理临时敏感数据:
try {
await sandbox.writeFile('/tmp/sensitive.txt', secretData);
await sandbox.exec('python process.py /tmp/sensitive.txt');
} finally {
await sandbox.deleteFile('/tmp/sensitive.txt');
}直接将凭证传递给 sandbox——通过环境变量或文件——意味着 sandbox 进程持有任何在其中运行的代码都可以读取的有效凭证。Worker 代理通过将凭证仅保留在 Worker 中,并向 sandbox 提供短期 JWT,消除了这种暴露。
流程如下:
Sandbox (short-lived JWT) → Worker proxy (validates JWT, injects real credentials) → External APIsandbox 永远不会看到真实凭证。如果 JWT 被泄露,它会在短窗口后过期且无法重用。
当访问 GitHub 进行私有仓库操作、AI 服务或对象存储,且你希望将凭证完全排除在容器之外时,此模式很有用。完整实现请参阅将请求代理到外部 API。
- Sandbox 到 sandbox 的访问(VM 隔离)
- 资源耗尽(强制配额)
- 容器逃逸(基于 VM 的隔离)
- 认证和授权
- 输入验证和清理
- 速率限制
- 应用级安全(SQL 注入、XSS 等)
使用单独的 sandbox 进行隔离:
const sandbox = getSandbox(env.Sandbox, `user-${userId}`);验证所有输入:
const safe = input.replace(/[^a-zA-Z0-9._-]/g, '');
await sandbox.exec(`command ${safe}`);对密钥使用环境变量:
await sandbox.startProcess('node app.js', {
env: { API_KEY: env.API_KEY }
});清理临时资源:
try {
const sandbox = getSandbox(env.Sandbox, sessionId);
await sandbox.exec('npm test');
} finally {
await sandbox.destroy();
}- Containers 架构 - 底层平台安全
- Sandbox 生命周期 - 资源管理