Как безопаснее всего выполнять недоверенный пользовательский код Node.js или Python на моем бэкенде?
Запускайте недоверенный пользовательский код в одноразовой среде выполнения вне вашего веб-бэкенда. Для публичного сервиса, который принимает произвольный код Node.js или Python, надежный базовый вариант — правильно настроенная микро-ВМ для каждой задачи или сеанса отдельного клиента. Не передавайте в нее учетные данные продакшена, блокируйте ненужный доступ к сети и задавайте лимиты ресурсов и срок действия извне среды выполнения.
Используйте приложение на Express, FastAPI или Django для аутентификации пользователей, постановки задач в очередь и возврата результатов. Поручите управление выполнением отдельному воркеру. Одних ограничений на уровне языка недостаточно для изоляции, которая нужна этой архитектуре.
Выбор конфигурации зависит от того, что могут отправлять пользователи. Калькулятору достаточно небольшого языка выражений. Агенту для работы с кодом, который устанавливает пакеты и выполняет команды оболочки, нужна полноценная среда выполнения. Мы рассмотрим второй случай, включая код, который ИИ создает на основе пользовательского ввода.
Сначала выберите границу изоляции
| Подход | Что он разделяет | Пригодность для произвольного пользовательского кода |
|---|---|---|
node:vm, Python eval или exec | Контексты языка или области выполнения | Недостаточно в качестве границы безопасности |
| Дочерний процесс | Память и жизненный цикл процессов | По-прежнему нужны ограничения ОС, файловой системы, сети и ресурсов |
| Обычный Docker | Пространства имен, представления файловой системы и группы ресурсов | Полезные средства контроля, но контейнеры используют общее ядро хоста Docker |
| gVisor | Ядро приложения обрабатывает системные вызовы до того, как они достигнут хоста | Подходит, если совместимость и производительность отвечают требованиям задачи |
| Микро-ВМ | Гостевое ядро и граница виртуальной машины | Надежный базовый вариант для вредоносного кода; хост и гостевую систему все равно нужно обновлять |
Безопасность Docker зависит от хоста, демона и конфигурации. gVisor добавляет ядро приложения в пространстве пользователя. Firecracker использует KVM для запуска микро-ВМ. Ни один из этих вариантов не определяет, какие учетные данные или сетевые адреса должно предоставлять ваше приложение. Безопасность Docker, архитектура gVisor, Firecracker.
При более строгих требованиях рассмотрите также выделенные хосты или аккаунты для воркеров. Граница ВМ снижает риски общего ядра, но не гарантирует, что любой выход из изоляции или аппаратный побочный канал невозможен.
Почему Node.js и Python нужна внешняя песочница
Node.js: контекст VM не служит границей безопасности
В документации Node прямо сказано, что node:vm нельзя использовать как механизм защиты от недоверенного кода. Передача пустого объекта или тайм-аута в vm.runInNewContext этого не меняет. Документация Node.js VM.
Рабочие потоки помогают выполнять задачи параллельно. Дочерний процесс может защитить веб-сервер от обычного сбоя интерпретатора. Ни то ни другое не закрывает автоматически доступ к файловой системе, унаследованным переменным окружения или сети. Дочерний процесс с правами вашего сервера в ОС по-прежнему имеет доступ к ресурсам, которые разрешают эти права.
В описании Permission Model в Node также сказано, что она не гарантирует защиту от вредоносного кода. Используйте ее как дополнительное ограничение там, где это полезно, а для вредоносного пользовательского кода задавайте внешнюю границу изоляции. Разрешения Node.js.
Python: ограничение имен не изолирует процесс
Python предупреждает, что exec и eval выполняют произвольный код. Удаление __builtins__ или блокировка нескольких импортов не превращают Python без ограничений в безопасный сервис выполнения кода. RestrictedPython также описывает подмножество языка и не утверждает, что сам по себе предоставляет песочницу. Встроенные функции Python, RestrictedPython.
Для данных принимайте JSON-документ ограниченного размера и проверяйте его структуру. ast.literal_eval не выполняет произвольный код Python, но документация Python предупреждает, что вредоносный ввод может исчерпать память, ресурсы CPU и стек. Кроме того, эта функция не поддерживает произвольные программы. Документация Python AST.
Для полноценного Python запускайте отдельный интерпретатор внутри выбранной песочницы. Разбор веб-запросов и аутентификацию пользователей выполняйте за ее пределами.
Архитектура бэкенда для выполнения недоверенного кода
Бэкенд должен управлять записью о задаче и правилами доступа:
- Аутентифицируйте отправителя и проверьте его квоту. Ограничьте размер запроса в байтах до буферизации исходного кода.
- Сохраните исходный код как данные. Привяжите задачу к клиенту и выберите среду выполнения из фиксированного списка разрешенных вариантов.
- Поставьте задачу в очередь. Доверенный воркер создает новую песочницу с явно заданными лимитами и сроком действия.
- Передайте только входные данные задачи. Выполните фиксированную команду интерпретатора, передав исходный код через файл или stdin.
- Соберите вывод в пределах лимита и статус. Проверьте результаты перед возвратом или использованием в другом сервисе.
- Остановите и удалите среду выполнения. Отдельный воркер очистки обрабатывает брошенные задачи.
Храните ключ провайдера песочниц, сокет Docker и учетные данные для управления облаком в доверенном воркере. Пользовательский код не должен получать учетные данные, которые позволяют создавать другие песочницы или управлять ими.
Проверяйте принадлежность клиенту при каждой операции: выполнении, отмене, скачивании, возобновлении и удалении. Идентификатор песочницы, который передал клиент, — это идентификатор, а не доказательство права доступа. Более общий процесс выполнения описан в нашем руководстве по песочницам для ИИ.
Какие ограничения задать до первой пользовательской задачи
| Ограничение | Начальная политика для небольшого сервиса выполнения кода |
|---|---|
| CPU и RAM | Фиксированный объем ресурсов; отклоняйте переданные пользователем флаги ресурсов |
| Полное время выполнения | Короткий срок выполнения, заданный извне, и максимальное время жизни песочницы |
| Процессы и файлы | Лимиты числа процессов, открытых файлов и доступного для записи хранилища |
| Ввод и вывод | Например, 32 KiB исходного кода и 64 KiB суммарного stdout/stderr; выберите лимиты для своего продукта |
| Файловая система | Базовый образ только для чтения и небольшой временный каталог; не монтируйте домашний каталог или приложение с хоста |
| Учетные данные | Никаких секретов продакшена; только при необходимости предоставляйте краткосрочный доступ с узкими правами |
| Сеть | По умолчанию запрещайте исходящий доступ; явно разрешайте нужные адреса |
| Состояние клиента | Новое рабочее пространство для каждой задачи или сеанс, привязанный к одному клиенту |
| Использование аккаунта | Лимиты параллельных задач, частоты отправки и расходов для каждого пользователя |
Выделенные ресурсы CPU ограничивают скорость выполнения бесконечного цикла. Ограничение по реальному времени определяет, как долго он сможет работать. Применяйте оба ограничения. Документация Docker указывает, что контейнеры не имеют ограничений CPU или памяти, пока вы их не настроите. Ограничения ресурсов Docker.
Сетевая политика должна охватывать внутренние сервисы и метаданные облака наряду с публичным интернетом. Если задачам нужен HTTP, применяйте список разрешенных адресов на исходящем прокси-сервере или сетевом уровне, который гостевая система не может изменить. Проверяйте разрешение DNS, адреса назначения IPv4/IPv6 и перенаправления. Обертка над fetch внутри гостевой системы не может контролировать каждую программу, которую эта система способна запустить. Предотвращение SSRF по OWASP.
Проверенные примеры запуска Docker для Node.js и Python
Эти команды показывают ограничения при запуске контейнера на фиксированных примерах программ. Они не включают внешний процесс контроля времени выполнения и объема вывода, необходимый для кода от пользователей публичного сервиса. Запускайте их на воркере выполнения, отдельном от серверов приложения. Для публичного сервиса выполнения вредоносного кода поместите этот воркер в выбранную ВМ или используйте подходящую среду выполнения песочницы.
Используйте доверенный образ с установленными обновлениями безопасности. Теги ниже удобны для примера; при развертывании фиксируйте проверенный дайджест образа и обновляйте его в рамках обычного процесса установки исправлений. Заранее скачивайте образы, прежде чем принимать задачи.
Пример для Node.js
docker run --rm -i --init \
--network none --read-only \
--tmpfs /tmp:rw,noexec,nosuid,nodev,size=16m \
--user 65534:65534 --cap-drop ALL \
--security-opt no-new-privileges \
--memory 128m --memory-swap 128m --cpus 0.5 \
--pids-limit 32 --ulimit nofile=64:64 \
--log-driver none \
node:24-bookworm-slim node - <<'JS'
console.log(JSON.stringify({answer: 6 * 7}));
JSОжидаемый вывод — {"answer":42}. Команда работает с UID 65534, сбрасывает Linux capabilities, оставляет корневую файловую систему доступной только для чтения и разрешает запись в небольшой /tmp. Она не предоставляет внешний сетевой интерфейс и не монтирует каталоги хоста.
Пример для Python
docker run --rm -i --init \
--network none --read-only \
--tmpfs /tmp:rw,noexec,nosuid,nodev,size=16m \
--user 65534:65534 --cap-drop ALL \
--security-opt no-new-privileges \
--memory 128m --memory-swap 128m --cpus 0.5 \
--pids-limit 32 --ulimit nofile=64:64 \
--log-driver none \
python:3.13-slim-bookworm python -I -B -u - <<'PYCODE'
import json
print(json.dumps({"answer": 6 * 7}))
PYCODEОжидаемый вывод — {"answer": 42}. Параметр Python -I уменьшает влияние путей импорта и окружения интерпретатора. Он не мешает программе использовать ресурсы ОС, которые разрешает ее контейнер. Параметры командной строки Python.
Сохраните стандартный профиль seccomp в Docker. no-new-privileges блокирует повышение привилегий через такие механизмы, как исполняемые файлы с setuid. Режим --network none оставляет только петлевой интерфейс. У этих ограничений разные задачи; ни одно не заменяет остальные. Параметры Docker run, seccomp, сетевая изоляция.
Параметр noexec временного каталога запрещает прямой запуск файлов с этой точки монтирования, но интерпретатор по-прежнему может прочитать и выполнить хранящийся там скрипт. Он не запрещает пользовательские программы. Лимит 128 MiB также подходит для этих небольших примеров, но не для каждого пакета npm или задачи анализа данных.
Подключение к бэкенду без подстановки в команды оболочки
На доверенном воркере формируйте запрос к Docker или провайдеру из фиксированных аргументов. В Node можно использовать spawn с shell: false; в Python — subprocess.Popen со списком аргументов. Передавайте присланный исходный код через stdin или файл по фиксированному пути. Никогда не помещайте его внутрь команды оболочки, например в строку, переданную в sh -c. Смотрите дочерние процессы Node и безопасность subprocess в Python.
Разрешайте пользователям выбирать среду выполнения по имени из утвержденного списка. Не позволяйте передавать в запросе флаги Docker, произвольные образы, пути хоста или имена переменных окружения. Установка пакетов тоже выполняет код; если вы ее поддерживаете, применяйте к ней те же правила изоляции и ограничения ресурсов.
Останавливайте задачу, ограничивайте вывод и подтверждайте очистку
Тайм-аут HTTP-запроса или вызова SDK может прекратить ожидание на бэкенде, пока удаленная программа продолжает работать. Аналогично, завершение локального процесса Docker CLI может оставить его контейнер работающим.
Доверенный процесс контроля выполнения должен:
- Записать идентификатор контейнера или песочницы до начала выполнения.
- Читать stdout и stderr по мере поступления с общим лимитом байтов.
- При истечении времени или превышении объема вывода останавливать саму задачу через API среды выполнения.
- Закрывать подключенные потоки или вычитывать их, не сохраняя неограниченный объем данных.
- Подтверждать завершение, удалять временное состояние и записывать все ошибки очистки.
Параметр --log-driver none в примерах не дает Docker сохранять логи контейнера. Ваш бэкенд все равно должен ограничивать объем читаемых потоков. В Docker останавливайте контейнер по имени до завершения подключенного CLI. Для сервиса микро-ВМ используйте операцию завершения из его документации и проверяйте конечное состояние.
Задайте срок действия на стороне провайдера, если он поддерживает эту возможность. Блок finally не выполнится после отключения питания воркера, поэтому запускайте по расписанию процесс очистки, который сверяет записи о просроченных задачах с работающими ресурсами. Сделайте повторную очистку безопасной. Не отмечайте задачу без управляющего процесса как успешно очищенную только потому, что клиент отключился.
Что мы проверили
31 октября 2026 года мы выполнили обе команды в Docker Desktop с Engine 28.1.1, ядром Linux 6.10.14-linuxkit, ARM64, Node.js 24.22.2 и Python 3.13.18.
| Проверка | Результат для обеих сред выполнения |
|---|---|
| Фиксированное вычисление | Возвращено 42 |
| Эффективный пользователь | UID 65534 |
Запись в /etc | Заблокирована |
Запись и чтение в /tmp | Работали |
| Исходящий сокет к тестовому адресу | Ошибка ENETUNREACH |
| Тестовая метка в окружении только на хосте | Отсутствовала внутри контейнера |
| Конфигурация ресурсов | 128 MiB памяти, без swap, 32 процесса/потока, половина CPU |
| Политика привилегий | NoNewPrivs включен |
| Бесконечный цикл под контролем тестового процесса | Остановлен по истечении двухсекундного срока |
| Избыточный вывод под контролем тестового процесса | Сбор остановлен на 64 KiB; выполнение задачи прекращено |
| Новый контейнер после предыдущей задачи | Предыдущий временный файл отсутствовал |
Наша первая проверка ограничения вывода выявила ошибку очистки: подключенный Docker CLI ожидал освобождения заполненного канала после остановки контейнера. Мы исправили тестовый процесс контроля, чтобы он сначала останавливал задачу, а затем завершал подключенный клиент. Поэтому одной команды запуска недостаточно для полноценного сервиса выполнения кода.
Эти проверки охватывают локальную конфигурацию контейнера и поведение процесса контроля. Они не проверяют выход из изоляции через ядро, не доказывают блокировку всех путей доступа и не тестируют изоляцию микро-ВМ Lizard. Повторите проверки в той среде выполнения воркера, которую вы развертываете.
Считайте результаты и сохраненное состояние недоверенными
Песочница может изолировать программу, но ее вывод при этом способен атаковать следующую систему. Экранируйте stdout перед отображением. Проверяйте возвращаемый JSON. Никогда не десериализуйте присланный Python pickle и не выполняйте на бэкенде возвращенную строку JavaScript. Предупреждение о Python pickle, обработка вывода по OWASP.
Отдавайте созданный HTML и страницы предпросмотра с источника (origin), который не использует общие учетные данные с вашей панелью управления. Для скачиваемых файлов контролируйте имена, размеры и типы; не переходите по созданным гостевой системой символическим ссылкам к путям хоста. Также ограничивайте объем работы при извлечении архивов и распаковке данных.
Чистое начальное состояние не дает одному клиенту читать файлы, кеши пакетов или значения в памяти другого клиента. Постоянным сеансам нужны явные правила принадлежности и хранения. Приостановленная песочница сохраняет состояние; она не должна становиться чистой средой следующего пользователя. Применяйте те же правила к снимкам состояния и общим томам.
Как использовать Lizard
Lizard Sandboxes выполняет код в микро-ВМ Firecracker, каждая со своим ядром. Lizard SDK поддерживает JavaScript/TypeScript и Python, включая операции с файлами, выполнение команд, проверки статуса и управление жизненным циклом. Ваш бэкенд может управлять средой выполнения через этот API вместо обслуживания собственных хостов воркеров. Документация Lizard Sandboxes.
Храните ключ API управления на бэкенде. Для задач с вредоносным кодом не передавайте lizardToken, который делает учетные данные проекта доступными внутри гостевой системы, и не подключайте учетные данные агентов, которые задаче не нужны. Явно задавайте время жизни и завершайте одноразовые задачи после сбора результатов в пределах лимита. Справочник Lizard SDK.
Изоляция Firecracker не означает блокировку исходящего трафика. Проверьте, что действующая сетевая политика ограничивает доступ требуемыми адресами. Текущий справочник параметров создания в SDK не описывает список разрешенных исходящих адресов; не придумывайте флаг и не считайте, что настройка прокси внутри гостевой системы обеспечивает такое ограничение. Если строгий контроль исходящего трафика обязателен, проверьте конфигурацию, которая способна его обеспечить, прежде чем принимать такую задачу.
Сначала определите разрешенные для задачи файлы, доступ к сети и бюджет ресурсов. Затем протестируйте новую среду с этими ограничениями. Используйте краткое руководство Lizard для работы с API выполнения и наше сравнение цен на песочницы для оценки затрат.
Разрабатывайте с ИИ. Развёртывайте с Lizard.
Вам не нужна платформенная команда, чтобы выйти в продакшн. Весь ваш облак — в одной команде CLI.
- Сервисы
- —
- Аддоны
- —
- Развёртывания
- —
- Запуски sandbox
- —