跳转到内容
搜索文档

安全模型

最后更新 查看 MarkdownAgent 设置

Sandbox SDK 基于 Containers 构建,每个 sandbox 在自己的 VM 中运行,以实现强隔离。

容器隔离

每个 sandbox 在单独的 VM 中运行,提供完整隔离:

  • 文件系统隔离 - Sandbox 无法访问其他 sandbox 的文件
  • 进程隔离 - 一个 sandbox 中的进程无法查看或影响其他 sandbox
  • 网络隔离 - Sandbox 具有单独的网络栈
  • 资源限制 - 按 sandbox 强制执行 CPU、内存和磁盘配额

有关底层容器平台的完整安全详情,请参阅 Containers 架构

在 sandbox 内

单个 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 访问

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

Preview URL 包含随机生成的令牌。任何拥有该 URL 的人都可以访问该服务。

要撤销访问,取消暴露端口:

await sandbox.unexposePort(8080);

快速隧道 URL

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');
}

代理外部 API 请求

直接将凭证传递给 sandbox——通过环境变量或文件——意味着 sandbox 进程持有任何在其中运行的代码都可以读取的有效凭证。Worker 代理通过将凭证仅保留在 Worker 中,并向 sandbox 提供短期 JWT,消除了这种暴露。

流程如下:

Sandbox (short-lived JWT) → Worker proxy (validates JWT, injects real credentials) → External API

sandbox 永远不会看到真实凭证。如果 JWT 被泄露,它会在短窗口后过期且无法重用。

当访问 GitHub 进行私有仓库操作、AI 服务或对象存储,且你希望将凭证完全排除在容器之外时,此模式很有用。完整实现请参阅将请求代理到外部 API

SDK 防护的内容

  • 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();
}

相关资源

这篇文档对您有帮助吗?