OpenAI построила безопасную песочницу для Codex на Windows
Когда я присоединился к команде разработки Codex в сентябре 2025 года, у Codex для Windows не было реализации песочницы, и пользователям Windows приходилось выбирать между двумя неудобными вариантами при работе с кодирующими агентами OpenAI:
Подтверждать почти каждую команду — даже чтение, — которую агент хотел запустить. Это неэффективно и раздражает. Одно из главных преимуществ Codex как раз в том, что вам не нужно выполнять всю рутинную работу вручную.
Включить режим Full Access: разрешить Codex запускать все команды без подтверждений и ограничений. Это снижает трение, но делает контроль слабее.
Codex, наш кодирующий агент, работает на ноутбуках разработчиков — через CLI, расширение для IDE или десктопное приложение. Он управляет диалогом между человеком за клавиатурой и моделью, работающей в облаке и выполняющей инференс.
По умолчанию Codex запускается с правами реального пользователя, то есть может делать все, что может сам пользователь. Это мощно и потенциально опасно. Кодирующая модель может поручать harness выполнять локальные команды — от запуска тестов до чтения или редактирования файла и создания ветки Git, — поэтому режим по умолчанию старается найти баланс между эффективностью и безопасностью. В этом режиме Codex может читать файлы почти отовсюду и записывать файлы в пределах workspace, то есть каталога, из которого он запущен, а доступа в интернет нет, если пользователь сам его не разрешит. Чтобы автоматически ограничивать запись файлов и сетевой доступ безопасными рамками, Codex нужна среда песочницы, которая действительно применяет эти ограничения.
Sandbox — это ограниченная среда исполнения. Когда разработчик использует Codex, операционная система компьютера запускает команду с пониженными правами, а эти ограничения распространяются по всему дереву процессов. Каждая команда Codex изначально запускается в песочнице, и каждый дочерний процесс остается в тех же границах.
Для эффективной песочницы Codex нужны механизмы изоляции, которые обеспечивает сама операционная система. Некоторые ОС дают хорошие инструменты для этого, например Seatbelt в macOS, seccomp или bubblewrap в Linux; однако в Windows такого встроенного механизма сейчас нет.
Чтобы сделать Codex на Windows таким же безопасным и удобным, как и на других платформах, нам пришлось реализовать собственную песочницу.
Где существующие инструменты Windows не подошли
В Windows есть некоторые инструменты и примитивы для изоляции. Ни один из них не соответствовал нашим требованиям полностью, но мы рассмотрели несколько вариантов: AppContainer, Windows Sandbox и Mandatory Integrity Control.
AppContainer
- Что это: нативная песочница Windows, модель изоляции на основе capabilities, созданная для приложений, которые заранее знают, к каким ресурсам им нужен доступ.
- Почему привлекала: это настоящая граница на уровне ОС, а не набор ограничений «по возможности».
- Почему не подошла: Codex — не одно узко очерченное приложение. Он поддерживает открытые сценарии разработки: shell, Git, Python, менеджеры пакетов, инструменты сборки и любые другие бинарники, которые агент сочтет нужными. На практике это делало AppContainer неправильной формой для задачи. Изоляция была сильной, но для гораздо более узкого класса нагрузок, чем «позволить агенту работать как разработчик».
Windows Sandbox
- Что это: одноразовая облегченная виртуальная машина Microsoft. Вы получаете чистый рабочий стол Windows с сильной границей изоляции, а все, что вы делаете внутри, исчезает после завершения сеанса.
- Почему привлекала: очевидно, она гораздо лучше совместима с произвольным ПО, чем AppContainer, и с точки зрения безопасности это намного более надежная коробка.
- Почему не подошла: Codex должен работать напрямую с реальным checkout пользователя, его инструментами и окружением, а не в отдельном временном рабочем столе, который пришлось бы настраивать и связывать с хостом. Кроме того, у этого был фундаментальный продуктовый минус: Windows Sandbox вообще недоступна в редакциях Windows Home.
Mandatory Integrity Control (MIC), маркировка целостности
- Что это: в Windows есть понятие «уровни целостности» — низкий, средний и высокий, — которые определяют, насколько система доверяет объектам и процессам. Базовое правило такое: процесс с более низким уровнем целостности не может записывать в объект с более высоким уровнем, даже если обычный ACL это позволял бы. Например, процесс с низкой целостностью считается менее доверенным, поэтому Windows блокирует ему запись в обычные объекты со средней целостностью, если эти объекты специально не пере-маркированы для такого доступа.
- Почему привлекала: на бумаге MIC выглядел элегантно — запустить Codex с низкой целостностью, пере-маркировать writable roots как low integrity и позволить Windows запрещать запись везде остальном. Это дало бы нам путь без admin-прав и с настоящим механизмом на уровне ОС.
- Почему не подошла: как и ACL, метки целостности меняют реальную файловую систему хоста, и в этом случае семантическое изменение особенно широкое. Пометить workspace как low integrity означает не просто «Codex может писать сюда». Это означает, что процессы с low integrity вообще могут писать туда. На реальной машине разработчика это превращает фактический checkout пользователя в low-integrity sink для хоста, что гораздо рискованнее, чем выдавать тщательно таргетированные ACL одной песочнице. Даже если среднецелевые инструменты разработки продолжат работать, базовая модель доверия рабочего пространства меняется так, что это сложно ограничить и еще труднее обосновать.
Оценив все варианты и посчитав их непригодными, мы начали проектировать собственное решение, чтобы дать пользователям Windows хороший опыт работы с Codex.
Первый прототип: «неповышенная песочница»
Наш первый рабочий прототип использовал комбинацию концепций и инструментов Windows для реализации нужной изоляции. С самого начала одной из целей было обойтись без elevation, то есть Codex не должен был запрашивать у пользователя права администратора только для настройки или запуска песочницы. Это означало, что нужно было разумно ограничить две вещи: запись файлов и сетевой доступ.
Если не ограничить запись файлов вовсе, возникнет проблема безопасности. Если ограничить ее слишком сильно, песочница будет мешать продуктивности, постоянно запрашивая подтверждения. Чтобы решить эту задачу, мы опирались на два важных строительных блока Windows: SID и write-restricted tokens.
SID, или security identifier, — это идентификатор, с которым Windows связывает права доступа. У каждого пользователя есть SID, у групп есть SID, и даже у одной сессии входа свой SID. Например, у текущей активной сессии может быть SID вида S-1-5-5-X-Y. SID, назначенный локальной группе администраторов, может выглядеть как S-1-5-32-544.
Windows также позволяет создавать синтетические SID, которые не соответствуют реальному пользователю, но все равно могут фигурировать в ACL (access control list), определяющих, кто может читать, писать или выполнять конкретные файлы и каталоги. Это делает SID удобным примитивом для нашей песочницы: мы можем создавать SID исключительно для использования песочницей Codex, не затрагивая ничего другого на машине.
Токены процесса — это объекты безопасности в Windows, которые определяют личность и привилегии запущенного процесса. Они определяют, какие действия процесс может выполнять. Write-restricted token — это особый тип токена процесса, который заставляет Windows выполнять дополнительную проверку прав при операциях записи.
Чтобы запись прошла успешно, должны выполниться две проверки:
Обычная идентичность пользователя (владелец токена) должна иметь право на это
Хотя бы одному SID в списке restricted SID токена также должен быть предоставлен доступ
На практике эти проверки позволяли нам использовать ACL, чтобы точно определить, где песочница может изменять файловую систему, и это дало нужную нам гранулярность для операций записи.
С SID и write-restricted token наша неповышенная песочница работала так:
При настройке песочницы создавался синтетический SID с именем sandbox-write.
SID sandbox-write получал права write, execute и delete для
Текущего рабочего каталога
Любых дополнительных writable_roots, настроенных в config.toml.
При настройке песочницы этому же SID явно запрещался доступ на запись в «read-only within writable» расположения, такие как:
<cwd>/.git
<cwd>/.codex
<cwd>/.agents
Codex запускал команды под write-restricted token, в чей список restricted SID входили Everyone, SID текущей вошедшей сессии и синтетический SID sandbox-write.
Этот механизм эффективно решил задачу ограничения записи файлов и выглядел многообещающе. Теперь нам нужно было ограничить сетевой доступ песочницы.
Ограничение сетевого доступа — важная часть песочницы; без него вредоносный код мог бы выносить данные с машины в интернет. Поскольку мы хотели избежать необходимости elevation, у нас было немного возможностей для жесткой блокировки сетевого трафика. Инструменты, которые мы хотели использовать, например Windows Firewall, обычно нельзя было установить без admin-прав.
Не имея возможности использовать Windows Firewall, мы ограничились тем, что могли контролировать. Мы старались сделать дочернюю среду fail-closed для тех сетевых инструментов, которыми реально пользуются разработчики, чтобы команды Git, установщики пакетов и так далее не работали в песочнице, а пользователю приходилось бы подтверждать любые операции, выходящие в интернет. Идея заключалась в том, чтобы отравить очевидные пути обхода: направить прокси-ориентированный трафик в мертвую точку, заставить HTTP(S)-транспорт Git делать то же самое и сделать Git over SSH немедленно падающим. Дополнительно мы помещали маленький каталог denybin в начало PATH и меняли порядок PATHEXT, чтобы заглушки SSH и SCP разрешались раньше настоящих бинарников.
Например, вот некоторые из конкретных переопределений среды, которые мы использовали для ограничения сетевого доступа:
- HTTPS_PROXY=http://127.0.0.1:9
- ALL_PROXY=http://127.0.0.1:9
- GIT_HTTPS_PROXY=http://127.0.0.1:9
- NO_PROXY=localhost,127.0.0.1,::1
- GIT_SSH_COMMAND=cmd /c exit 1
Это ловило большую часть обычного трафика, инициируемого инструментами, но все еще было лишь advisory. Процесс мог игнорировать переменные среды, обходить PATH или просто открывать сокеты напрямую — слишком рискованно.
Как и у любой интересной программной реализации, у первого прототипа были плюсы и минусы. Он справлялся с задачей, используя лишь несколько стандартных возможностей Windows, обеспечивал очень явные и гранулярные записи в файловую систему и работал без elevation — снижая необходимость для пользователей соглашаться на избыточные запросы прав администратора или быть admin на своей локальной машине, — но у него были и серьезные недостатки, некоторые из которых не позволили бы ему стать финальным дизайном:
- Скорость настройки: применение ACL к workspace может быть дорогим по времени в зависимости от топологии каталога workspace.
- След в системе: мы применяли реальные ACL к системе разработчика, хотя этот след не слишком инвазивен, поскольку все примененные ACL относятся к специально созданному синтетическому SID, используемому только песочницей.
- Сложно менять семантику: reliance on ACL для файловых ограничений означает, что изменить поведение песочницы дорого и сложно. Тогда как на macOS мы можем динамически менять способ генерации файла .sbpl, который используется для настройки Seatbelt, в Windows изменение песочницы может требовать медленной и тяжелой операции по изменению ACL.
- Защита сети слабая. Как уже было сказано, она была лишь «advisory», ее наверняка обходили бы некоторые программы с собственной сетевой реализацией, и она не была рассчитана на противодействие враждебному коду.
Первые три проблемы — естественное следствие собственной песочницы, достаточно гибкой для agentic-сценариев. История с подавлением сети была другой.
Помимо того, что вредоносный агент мог легко обойти suppression на основе переменных среды, многие доброкачественные бинарники тоже обходили бы его просто потому, что не учитывали proxy-переменные среды или использовали собственный сокетный сетевой код. Мы решили, что этого достаточно, чтобы вложиться в более сильный режим песочницы.
Чтобы добиться лучшего подавления сети, мы хотели использовать Windows Firewall, который позволяет блокировать исходящий сетевой трафик для пользователей или программ. К сожалению, мы не могли эффективно создать рабочее правило firewall, которое применялось бы только к командам, запускаемым harness Codex, по нескольким причинам:
- Windows не позволяет привязать правило firewall к непервичному идентификатору restricted token. Это значит, что мы не могли применить правило к «любому токену, который содержит наш синтетический SID в списке restricted SID».
- Хотя мы могли создать правило, совпадающее с конкретным бинарником, это позволяло ограничить сеть только для самого codex.exe. Такое правило не распространялось бы на процессы, которые агент запускает от имени пользователя, например Git или Python.
- Другие измерения соответствия firewall тоже не подходили. Правила, привязанные к пользователю, все равно совпадали бы с реальным пользователем Windows в неповышенной схеме, а не только с ограниченным дочерним процессом. Правила по пути к программе были слишком грубыми: они могли бы блокировать codex.exe или python.exe вообще, но не этот конкретный изолированный запуск python.exe. Правила по порту или адресу тоже решали не ту задачу. Например, нам не нужно было блокировать порт 443; мы хотели блокировать произвольный исходящий доступ для именно этого ограниченного дерева процессов.
Чтобы применить правило firewall именно к нашим изолированным командам, нам нужно было запускать их как отдельный principal, а не как «реального» пользователя. Этот подход привел нас на новый путь, где мы ослабили ограничение «без elevation».
Переработка: «повышенная песочница»
Следующая итерация песочницы, которая является нашей текущей реализацией, требует повышенных admin-прав на этапе настройки. Поэтому я называю ее «повышенной песочницей». На границе, где Codex запускает команду в системе, повышенная песочница выглядит так же, как и неповышенная. Она по-прежнему запускает дочерние процессы под restricted token — аналогично write_restricted token с тем же списком restricted SID [Everyone, Logon, Synthetic], — однако principal этого токена уже не является реальным пользователем Windows, а становится одним из двух локальных пользователей, созданных самим Codex:
- CodexSandboxOffline — пользователь, на которого нацелены правила firewall
- CodexSandboxOnline — пользователь, на которого правила firewall не нацелены
Эта на первый взгляд небольшая деталь на деле имеет большие последствия для песочницы, для того, кто может ею пользоваться, и для сложности ее настройки и выполнения.
Визуально она похожа на неповышенный прототип, но с появлением правил firewall и выделенного пользователя Windows, который и запускает команды. Однако появление этих новых понятий означает и больше работы по настройке до того, как песочница начнет запускать и защищать команды.
В неповышенной песочнице шаг настройки был простым, но довольно небольшим:
- создать синтетический SID, если нужно
- применить ACL для синтетического SID sandbox-write
У повышенной песочницы работы больше.
- Создать синтетический SID, если он еще не создан
- Создать онлайн- и офлайн-пользователей песочницы, если их еще нет
- Сохранить локально учетные данные новых пользователей и зашифровать их с помощью Windows Data Protection API (DPAPI) в месте, которое сами пользователи песочницы не могут прочитать
- Создать правила firewall, блокирующие весь исходящий сетевой доступ для пользователя CodexSandboxOffline, либо, если они уже существуют, проверить их корректность
На этапе настройки есть еще один нюанс. Ожидается, что песочница Codex будет иметь доступ на чтение, эквивалентный реальному пользователю Windows. В неповышенной песочнице, где principal SID в restricted token был SID самого Windows-пользователя, это достигалось. Но когда principal становится новым пользователем CodexSandbox, это уже не происходит само собой. Многие важные каталоги в Windows предоставляют права read/execute группе «Authenticated Users». Один из заметных примеров — каталог профиля пользователя. По умолчанию пользователи Windows не могут читать профили других пользователей Windows, поэтому даже простые чтения файлов во многих сценариях не сработали бы.
Чтобы решить это, мы добавили еще один слой в процесс настройки песочницы — слой, который выдает ACL на чтение пользователям песочницы там, где таких ACL может не быть. Например, для некоторых часто используемых каталогов Windows:
- C:\Users\<real-user>
- C:\Windows\
- C:\Program Files\
- C:\Program Files (x86)\
- C:\ProgramData\
Поскольку этот список каталогов best-effort, а установка ACL на каждый из них может быть довольно дорогой, мы запускаем эту логику асинхронно, чтобы блокирующий для пользователя этап настройки песочницы не ждал ее завершения.
Мы вынесли логику настройки в отдельный бинарник отчасти затем, чтобы пересекать границу UAC только когда это действительно нужно. Но более глубокая причина была архитектурной: настройка песочницы решает принципиально другую задачу, чем codex.exe. Выделение этой логики в отдельный бинарник позволило codex.exe оставаться обычным, неповышенным harness; не раздувать Windows-специфичной логикой настройки codex.exe на других платформах; отделить более долгую настройку от времени жизни основного процесса; и дать нам одно место, где можно реализовать все разные пути настройки, нужные песочнице.
Из-за того, как в Windows работают границы входа пользователя и token, мы не могли продолжать создавать restricted token и запускать процесс под ним так, как это делали в неповышенной песочнице. Чтобы действительно запускать команды от имени другого пользователя Windows, нашей первой идеей был следующий поток:
- codex.exe запускается как реальный пользователь Windows. Затем, последовательно, Codex:
- вызывает LogonUserW(…) для пользователя песочницы
- вызывает CreateRestrictedToken(…) на токене этого пользователя песочницы
- используя этот restricted token пользователя песочницы, вызывает CreateProcessAsUserW(…) для запуска финального дочернего процесса
На практике этот желаемый поток не сработал из-за привилегий, необходимых на CreateProcessAsUserW(…). Это означало, что codex.exe мог создать restricted token для пользователя песочницы, но не мог надежно запустить дочерний процесс с таким токеном со стороны реального пользователя. Нам нужен был процесс, который уже работал бы от имени пользователя песочницы, — тогда шаг ограничения и финальный запуск происходили бы на стороне пользователя песочницы, а не на стороне реального пользователя.
Это требование привело к появлению codex-command-runner.exe — нового бинарника, чья единственная задача состоит в том, чтобы создать restricted token и запустить запрошенную команду. Вместо того чтобы просить codex.exe выполнять весь путь самостоятельно (реальный пользователь → пользователь песочницы → restricted token → дочерний процесс), мы разделили поток на две части:
Часть 1
- codex.exe вызывает CreateProcessWithLogonW(…) для запуска codex-command-runner.exe от имени пользователя песочницы, еще без restricted token.
Часть 2
- Внутри runner-а OpenProcessToken(GetCurrentProcess(), …) открывает собственный token, который уже принадлежит пользователю песочницы.
- Runner вызывает GetTokenInformation(…) для извлечения logon SID песочницы, а затем CreateRestrictedToken(…) для построения финального restricted token.
- Все еще внутри runner-а он вызывает CreateProcessAsUserW(…) с этим restricted token, чтобы запустить реальный дочерний процесс.
Альберт Эйнштейн говорил: «Все следует делать настолько простым, насколько возможно, но не проще». В этом духе наш дизайн достаточно хорошо решил каждую из задач. Финальная архитектура включает четыре слоя, которые мы уже упоминали:
- сам codex.exe
- codex-windows-sandbox-setup.exe для всей работы по повышенной настройке
- codex-command-runner.exe для запуска команд под restricted token
- дочерний процесс
Когда я впервые пришел в этот проект, я не представлял, к чему он приведет. Мой подход заключался в том, чтобы начать с инструментария песочницы на границе между Codex и операционной системой. Это очень похоже на то, как песочница Codex реализована на macOS и Linux.
По мере того как я узнавал больше о конкретных инструментах Windows и принимал десятки решений, балансируя безопасность и удобство использования, система выросла до нынешнего вида — несколько бинарников, собственные пользователи, правила firewall, этап повышенной настройки, асинхронные процессы и многое другое.
Это не особенно простая система, но каждая часть сложности появилась по необходимости — чтобы построить песочницу, которая одновременно безопасна и, насколько возможно, не мешает пользователю.
Баланс между безопасностью и реальной полезностью
Работая над хорошим пользовательским опытом для пользователей Codex на Windows, мы стремились создать безопасное решение, которое не жертвует полезностью: вся идея Codex в том, чтобы агенты могли делать работу без вашего постоянного внимания.
Один из главных уроков этого проекта заключался в том, что Windows не дала нам один примитив, который бы напрямую соответствовал понятию «безопасный автономный кодирующий агент». Мы собрали несколько инструментов и концепций вместе, чтобы получить связную систему. Некоторые ранние идеи оказались тупиковыми. Финальный дизайн стал гибридом более ранних прототипов, каждый из которых решал свою часть задачи.
Другой урок состоит в том, что безопасность для кодирующего агента — это совсем не то же самое, что классическая безопасность приложений. Codex должен работать в реальных сценариях разработки. Инженерная работа сводилась к поиску баланса между совместимостью с agentic-нагрузками и реальным принуждением к ограничениям. Именно это напряжение сформировало компромиссы в финальном дизайне.
Хотите увидеть песочницу Codex в действии? Попробуйте сами.
Материал — перевод статьи с английского.
Оригинал: Building a safe, effective sandbox to enable Codex on Windows