INTEGRITY Документация

Модель безопасности

Sandbox SDK работает на основе Containers, которые запускают каждую песочницу в отдельной VM для надежной изоляции.

Изоляция контейнера

Каждый sandbox выполняется в отдельной VM, обеспечивая полную изоляцию:

Полное описание вопросов безопасности базовой платформы контейнеризации см. в Архитектура контейнеров.

Внутри sandbox

Весь код в рамках одного 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');

Аутентификация

Доступ к песочнице

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 URLs

Preview URL содержат случайно сгенерированные токены. Любой, у кого есть этот URL, может получить доступ к сервису.

Чтобы отозвать доступ, закройте доступ к порту:

await sandbox.unexposePort(8080);

URL быстрых туннелей

Быстрые туннели (sandbox.tunnels.get(port)) возвращают *.trycloudflare.com URL со случайным именем хоста, которое присваивает Cloudflare: отдельного токена доступа не существует. Само имя хоста и служит контролем доступа, поскольку сервис доступен любому, кто знает URL. Чтобы отозвать доступ, удалите туннель:

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 for values the sandbox process needs
await sandbox.startProcess('node app.js', {
  env: {
    API_KEY: env.API_KEY,  // From Worker environment binding
  }
});

Учётные данные внешнего API, которые песочнице не нужно читать напрямую, храните в Worker и передавайте через обработчик исходящих запросов.

Очистка временных конфиденциальных данных:

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 в песочницу напрямую, например через переменные окружения или файлы, процесс песочницы получает действующий доступ к учётным данным, который может прочитать любой код, выполняющийся внутри неё. Исходящие обработчики устраняют этот риск: они хранят учётные данные в Worker и добавляют их в исходящие запросы уже там.

Процесс выглядит следующим образом:

Sandbox request → Outbound handler (injects real credentials) → External API

Песочница никогда не видит реальные учётные данные. Обновите секрет в окружении вашего Worker, и каждый запрос будет использовать новое значение.

Такой подход полезен при обращении к GitHub для операций с приватными репозиториями, к сервисам ИИ или к объектному хранилищу, если вы не хотите, чтобы учётные данные попадали в контейнер. Подробности реализации см. в Обработка исходящего трафика.

От чего защищает SDK

Что нужно реализовать

Рекомендации

Используйте отдельные песочницы для изоляции:

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