Windows 上で Codex を安全かつ効果的に動作させるためのサンドボックス構築
OpenAI は、Windows 環境における Codex の安全性と利便性を向上させるため、既存のツールでは不十分だった独自サンドボックス実装の開発を発表した。
キーポイント
Windows における従来の課題
Codex はデフォルトでユーザー権限を持つため、安全な実行には制約が必要だが、Windows には Mac や Linux のような標準的な分離機能(Seatbelt, seccomp など)が用意されていない。
既存ツールの限界
AppContainer や Windows Sandbox などの既存の Windows ツールを検討したが、Codex が求めるプロセスツリー全体への制約伝播や細かな制御には完全には適合しなかった。
独自サンドボックスの実装
OpenAI は OS の機能に依存せず、独自のサンドボックスを実装することで、ファイル書き込みのワークスペース限定やネットワークアクセスの制限など、安全かつ効果的な実行環境を構築した。
開発者体験の向上
この実装により、Windows ユーザーはコマンドごとの承認作業(非効率)や無制限のアクセス権限付与(危険)という二つの悪い選択肢を避け、他の OS と同等の安全で快適な利用が可能になった。
影響分析・編集コメントを表示
影響分析
この記事は、AI エージェントがローカル環境で安全に動作するための基盤技術における重要な進展を示しています。特に Windows という巨大な市場において、セキュリティと利便性のバランスを解決したことは、開発者コミュニティ全体にとって実用的な恩恵をもたらします。今後は、OS 固有の制約を超えたクロスプラットフォームでの AI エージェント運用がさらに加速すると予想されます。
編集コメント
OS の機能不足を自社の独自実装で補完する姿勢は、AI エージェントの実用化において不可欠な技術的成熟を示しています。Windows ユーザーにとっての障壁が取り除かれたことは、開発者ツールの普及における重要なマイルストーンです。
2025 年 9 月に Codex エンジニアリングチームに参加した際、Windows 版 Codex にはサンドボックス実装が存在せず、その結果、Windows ユーザーは OpenAI のコーディングエージェントを利用する際に、以下のいずれかという劣った選択肢を迫られていました:
- コーディングエージェントが実行しようとするほぼすべてのコマンド(読み取り操作を含む)に承認を与えること。これは非効率的で煩わしい行為です。Codex を利用する大きな利点の一つは、こうした面倒な作業をすべて自分で行う必要がないことです。
- フルアクセスモードを有効化すること:Codex に承認や制限なくすべてのコマンドを実行させることで、監視の目を犠牲にして摩擦を取り除きます。
Codex は、開発者のノートパソコン上で動作するコーディングエージェントです。CLI(コマンドラインインターフェース)、IDE 拡張機能、またはデスクトップアプリを通じて利用可能です。これは、キーボードを操作する人間とクラウドで推論を実行するモデルとの間の対話を管理します。
Codex はデフォルトでは実在するユーザーの権限で実行されるため、そのユーザーができることは何でも実行可能です。これは強力である一方で潜在的に危険でもあります。コーディングモデルは、テストの実行からファイルの読み書き、Git ブランチの作成に至るまで、ハッチに対してローカルでコマンドを実行させる可能性があります。そのため、Codex のデフォルトモードでは、有効性と安全性の適切なバランスを見つけるよう努めています。このデフォルトモードでは、Codex はほぼあらゆる場所からファイルを読み取り、実行している Codex のディレクトリ(つまりワークスペース)内でのみファイルを書き込むことができます。ただし、ユーザーが明示的にインターネットアクセスを希望しない限り、ネットワークへの接続は行われません。ファイルの書き込みやネットワークアクセスを安全な範囲内で自動的に制限するためには、Codex はこれらの制約を実際に強制するサンドボックス環境を必要とします。
*サンドボックス*とは、制限された実行環境のことです。開発者が Codex を使用すると、コンピューターのオペレーティングシステムが権限を減らしたコマンドを開始し、その制約はプロセスツリー全体に伝播します。Codex のすべてのコマンドは開始時からサンドボックス化されており、すべての子プロセスも同じ境界内に留まります。
Codex が効果的なサンドボックスを実装するためには、コンピューターのオペレーティングシステムによって強制される分離機能が必要です。一部のオペレーティングシステムにはこれをうまく行うユーティリティが用意されています(例えば、macOS の Seatbelt、Linux の seccomp や bubblewrap など)。しかし、Windows には現在、このような機能が標準で提供されていません。
Codex を Windows でも他プラットフォームと同様に安全かつ快適に利用できるようにするため、独自のサンドボックスを実装する必要がありました。
既存の Windows ツールの限界
Windows には隔離のためのいくつかのツールやプリミティブが用意されています。これらはいずれも私たちの要件を完全に満たすものではありませんでしたが、私たちは複数の潜在的な解決策を検討しました。具体的には、AppContainer、Windows Sandbox、および必須整合性制御ラベルです。
AppContainer
- 概要:AppContainer はネイティブの Windows サンドボックスであり、アプリが事前にアクセスするものを正確に把握している場合に最適化された能力ベースの隔離モデルです。
- 利点:ベストエフォート型の制限ではなく、実際の OS の境界を提供するため魅力的です。
- 欠点:Codex は狭い範囲に限定された単一のアプリではありません。Codex はオープンエンドな開発ワークフローを駆動します。シェル、Git、Python、パッケージマネージャー、ビルドツール、そしてエージェントが必要と判断するその他のバイナリなどです。実用上、これは問題に対して AppContainer が不適切な形状であることを意味しました。確かに強力な隔離を提供しますが、「エージェントが開発者として動作できるようにする」というワークロードクラスに比べると対象範囲は非常に狭いものです。
Windows Sandbox
- What: Windows Sandbox はマイクロソフトの使い捨て型軽量仮想マシンです。強力な隔離境界を持つ新鮮な Windows デスクトップが提供され、セッション終了時に内部で行ったすべての操作が消去されます。
- Why: 明らかな理由から興味深い機能です—任意のソフトウェアとの互換性は AppContainer よりもはるかに高く、セキュリティの観点からははるかに堅牢な環境です。
- Why not: Codex はユーザーの実際のチェックアウト、ツール、環境に直接作用する必要があり、セットアップやホスト/ゲスト間のブリッジングが必要となる別の使い捨てデスクトップ内で動作することはできません。また、根本的な製品上の問題として、Windows Sandbox は Windows Home SKU では利用すらできないという点があります。
Mandatory Integrity Control (MIC) 整合性ラベリング
- What: Windows には「整合性レベル」と呼ばれる概念があり、低、中、高などのレベルが存在します。これはシステムがオブジェクトやプロセスをどの程度信頼するかを決定するものです。基本ルールとして、通常の ACL(アクセス制御リスト)が本来許可していたとしても、低い整合性レベルのプロセスは高い整合性レベルのオブジェクトに書き込むことはできません。例えば、低い整合性レベルのプロセスは信頼度が低いとみなされるため、明示的に書き込みを許可するように再ラベル付けされていない限り、Windows は通常の中等度整合性レベルのオブジェクトへの書き込みをブロックします。
- Why: MIC(マイクロソフトの統合コンテナ)は理論上は魅力的に見えました。Codex を低い整合性レベルで実行し、書き込み可能なルートディレクトリを低い整合性レベルに再ラベル付けし、それ以外の場所では Windows が書き込みを禁止するという仕組みです。これにより、実在する OS のメカニズムに支えられた非管理者権限のパスが実現できたはずです。
- Why not: ACL と同様に、整合性ラベルは実際のホストファイルシステムを変更します。この場合、その意味的な変更範囲は特に広範になります。ワークスペースを低い整合性レベルにマークすることは、「Codex がここに書き込める」という意味だけでなく、一般的な低い整合性レベルのプロセスがそこに書き込めるようになることを意味します。実際の開発者マシンにおいて、これはユーザーの実際のチェックアウト先をホストに対する低い整合性レベルの受け皿に変えてしまい、特定のサンドボックス設計に対して慎重にターゲットを絞った ACL を付与するよりもはるかにリスクが高くなります。中等度整合性レベルの開発ツールが引き続き動作したとしても、ワークスペースの根本的な信頼モデルは、 containment が難しく、正当化も困難な方法で変更されてしまいます。
すべての選択肢を検討した結果、どれも実現不可能であると判断されたため、Windows ユーザーに良好な Codex 体験を提供するための独自のソリューションの設計を開始しました。
最初のプロトタイプ:「権限昇格なしサンドボックス」
私たちの最初の動作可能なプロトタイプは、必要な分離を実現するために Windows の概念とツールの組み合わせを利用しました。最初から一つの目標は、*権限昇格(elevation)*を必要とせずにこれを機能させることでした。つまり、Codex がサンドボックスを設定または実行する際に、ユーザーに管理者権限の付与を求めるプロンプトを表示する必要がないようにすることです。そのためには、2 つの事柄に対して合理的な制限を設ける方法を検討する必要がありました。それは「ファイルへの書き込み」と「ネットワークアクセス」です。
もしファイルへの書き込みに全く制限を加えなければ、安全性の問題が生じます。逆に、ファイルへの書き込みを過度に制限すれば、サンドボックスがユーザーの生産性を損ない、常に承認を求める必要が出てきてしまいます。この問題を解決するために、私たちは 2 つの重要な Windows の構成要素に依存しました:SID と書き込み制限付きトークンです。
SID(セキュリティ識別子)とは、Windows が権限と結びつけるアイデンティティのことです。各ユーザーには SID が割り当てられ、グループにも SID が存在し、単一のログインセッション自体にも独自の SID が付与されます。例えば、現在ログイン中のセッションには S-1-5-5-X-Y のような SID が割り当てられる可能性があります。ローカル管理者グループに割り当てられた SID は S-1-5-32-544 となります。
SID(Security Identifier)は、Windows が権限と結びつけるアイデンティティです。各ユーザーには SID が割り当てられ、グループにも SID が存在し、単一のログインセッション自体にも独自の SID が付与されます。例えば、現在ログイン中のセッションには S-1-5-5-X-Y のような SID が割り当てられる可能性があります。ローカル管理者グループに割り当てられた SID は S-1-5-32-544 となります。
書き込み制限付きトークン(write-restricted token)は、プロセスがファイルシステムに対して行う操作を制御するためのメカニズムです。これにより、特定のディレクトリやファイルへの書き込みを許可しつつ、他の場所へのアクセスをブロックすることが可能になります。
Windows では、実際のユーザーに対応していない合成 SID を作成することもでき、これらは ACL(アクセス制御リスト)に表示されます。ACL は、特定のファイルやディレクトリを誰が読み書き実行できるかを定義するものです。このため、SID は当社のサンドボックスにとって有用な基本要素となります。Codex のサンドボックス専用として SID を作成し、マシン上の他の何ものにも干渉させることなく利用できます。
プロセストークンは、Windows におけるセキュリティオブジェクトであり、実行中のプロセスのアイデンティティと特権を定義します。これにより、プロセスが実行できるアクションが決定されます。「書き込み制限付きトークン」は、特定の種類のプロセストークンであり、書き込み操作に対して Windows が追加のアクセスチェックを実行させるものです。
書き込みが成功するためには、2 つのチェックが両方とも通過する必要があります:
- 通常のユーザーアイデンティティ(トークンの「所有者」)がその操作を許可されていること
- トークンの制限付き SID リスト内の少なくとも 1 つの SID もアクセス権限が付与されていること
実際には、これらのチェックにより、ACL を使用してサンドボックスがファイルシステムを修正できる場所を正確に定義でき、書き込み操作に関する必要な粒度を実現できました。
SID と書き込み制限付きトークンを用いることで、特権昇格なしのサンドボックスは以下のように動作しました:
- サンドボックス設定により、sandbox-write という合成 SID が作成されました。
- sandbox-write SID には、現在の作業ディレクトリおよび config.toml に追加で設定された書き込み可能なルートに対して、書き込み・実行・削除のアクセス権が付与されました。
- 一方、サンドボックス設定では、<cwd>/.git、<cwd>/.codex、<cwd>/.agents など、「書き込み可能領域内でも読み取り専用」とされる場所に対する同じ SID の書き込みアクセスを明示的に拒否しました。
- Codex は、Everyone、現在ログイン中のセッションの SID、および sandbox-write という合成 SID を制限された SID リストに含む、書き込みが制限されたトークン下でコマンドを実行します。
このフローによりファイル書き込みの制限が効果的に解決され、有望な結果となりました。次に必要なのは、サンドボックスのネットワークアクセスを制限するソリューションです。
ネットワークアクセスの制限はサンドボックスにおいて重要な要素であり、これを怠ると悪意のあるコードがマシンからデータをインターネットへ漏洩させる可能性があります。権限昇格の要件を避けたいという要望があったため、ネットワークトラフィックを強力にブロックできる選択肢は限られていました。Windows Firewall などの使用を検討したツールも、管理者権限なしではインストールできないのが一般的です。
Windows ファイアウォールをオプションとして利用できない場合、制御できる範囲は限られていました。開発者が実際に使用するネットワークツールの種類に対して、子環境が失敗時にクローズド(閉鎖的)になるように努めました。つまり、Git コマンドやパッケージインストーラーなどはサンドボックス内で失敗し、ユーザーがインターネットに接続する操作を承認する必要があるようにしました。この考え方は、明白な脱出経路を毒化することです:プロキシ対応のトラフィックを死んだエンドポイントへ送信し、Git の HTTP(S) トランスポートも同様に動作させ、SSH 経由の Git は即座に失敗するようにします。さらに、PATH に小さな denybin ディレクトリを先頭に追加し、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
これにより、多くの通常のツール駆動型トラフィックが捕捉されましたが、依然として勧告的な措置に過ぎませんでした。プロセスは環境変数を無視したり、PATH をバイパスしたり、あるいはソケットを直接開いたりできるため、リスクが高すぎます。
興味深いソフトウェア実装と同様に、最初のプロトタイプにも長所と短所がありました。標準的な Windows の機能のみでタスクを完了でき、非常に明示的で細粒度のファイルシステム書き込みが可能であり、権限昇格なしで実行できるため、ユーザーが過度な権限昇格のプロンプトを受け入れる必要やローカルマシンでの管理者権限が必要ないという点では優れていましたが、いくつかの重大な欠点があり、その一部は最終設計として採用できない理由となりました:
- セットアップの速度:ワークスペースディレクトリのトポロジーによっては、ワークスペースへの ACL(アクセス制御リスト)を適用するコストが高くなる可能性があります。
- 影響範囲:開発者のシステムに実際の ACL を適用しましたが、適用されたすべての ACL はサンドボックスのみが使用するカスタム作成の合成 SID に基づくものであるため、特に侵入性の高いものではありませんでした。
- 変更が困難なセマンティクス:ファイルベースの制限に ACL に依存しているため、サンドボックスのセマンティクスを変更するにはコストが高く複雑になります。macOS では Seatbelt を構成するために使用される.sbpl ファイルの生成方法を動的に変更できますが、Windows のサンドボックスでは ACL を調整するために時間がかかり、負荷の高い操作が必要になる可能性があります。
- ネットワーク保護が弱い。前述の通り、「勧告的」なものであり、独自のネットワークスタックを実装した一部のプログラムによって容易に回避され、敵対的なコードに対抗する設計ではありませんでした。
最初の 3 つの問題は、エージェントフローに対応できる柔軟性を持つカスタムサンドボックス実装に内在するものです。一方、ネットワーク抑制の仕組みについては事情が異なりました。
悪意のあるエージェントが環境ベースのネットワーク抑制を容易に回避できることに加え、多くの善意あるコードやバイナリも、環境プロキシ変数を尊重しない場合や、独自のソケットベースのネットワークコードを実装している場合に、単にそれらを回避してしまう可能性があります。私たちはこの側面が、より優れたサンドボックスモードへの投資を検討するのに十分であると判断しました。
より効果的なネットワーク抑制を実現するために、Windows Firewall(Windowsファイアウォール)を使用したいと考えていました。これにより、ユーザーやプログラムからの送信元ネットワークトラフィックをブロックすることが可能になります。残念ながら、Codex ハーネスによって起動されたコマンドにのみ適用される機能的なファイアウォールルールを効果的に作成することはできませんでした。その理由は主に以下の通りです:
- Windows では、制限されたトークンの非主権者 ID にファイアウォールルールを一致させることができません。これはつまり、「合成 SID を制限 SID リストに含むあらゆるトークン」に対してファイアウォールルールを適用できないことを意味します。
- 特定のバイナリにマッチするファイアウォールルールを作成することは可能ですが、それでは codex.exe 自体のネットワーク通信のみを制限できるに過ぎません。ユーザーに代わってエージェントが起動するプロセス(Git や Python プロセスなど)には適用されません。
- 他のファイアウォールの一致条件も、形状が合いませんでした。ユーザースコープのルールは、非特権設計においても実際の Windows ユーザーをマッチさせてしまい、制限された子プロセスだけを対象にできません。プログラムパスベースのルールは粗すぎます:codex.exe や python.exe 全体をブロックすることはできても、この特定のサンドボックス化された python.exe の呼び出しだけをブロックすることはできません。ポートやアドレスに基づくルールも、根本的にポリシーが合いません。例えば、ポート 443 をブロックしたいわけではなく、この特定の制限プロセスツリーに対する任意のアウトバウンドアクセスをブロックしたいのです。
サンドボックス化されたコマンドに特化したファイアウォールルールを適用するためには、それらを「実際の」ユーザーとしてではなく、別の主権者として実行する必要があります。このアプローチは、「特権昇格なし」という制約を緩和する新たな道へと私たちを導きました。
再設計:「特権昇格型サンドボックス」
サンドボックスの次の反復版、つまり現在の実装では、セットアップ時に管理者権限の昇格が必要です。そのため、私はこれを「昇格済みサンドボックス」と呼んでいます。Codex がシステム上でコマンドを起動する境界において、昇格済みサンドボックスは非昇格版と視覚的に同じように見えます。ただし、子プロセスは制限されたトークン([Everyone, Logon, Synthetic] という同じ制限付き SID リストを持つ write_restricted トークンと同様)の下で実行されますが、このトークンの主体は実際の Windows ユーザーではなく、Codex 自身が作成した以下の 2 つのローカルユーザーのいずれかになります:
- CodexSandboxOffline(ファイアウォールルールによって対象となるユーザー)
- CodexSandboxOnline(ファイアウォールルールによって対象とならないユーザー)
この一見小さな詳細は、サンドボックスの性質、誰が利用可能か、およびセットアップとランタイム実行の複雑さにおいて大きな影響を及ぼします。
これはファイアウォールルールの導入とコマンドを実行する専用の Windows ユーザーの追加により、非昇格版のプロトタイプと視覚的に似ていますが(ただし、これらの新しい概念の導入は、サンドボックスがコマンドの実行と保護を開始する前に、より多くのセットアップ作業が必要になることを意味します)。
非昇格版のサンドボックス設計には単純なセットアップ手順がありましたが、その規模は比較的小さかったです:
- 必要に応じて合成 SID を作成する
- サンドボックス用書き込み制限付き合成 SID の ACL(アクセス制御リスト)を適用する
一方、昇格済みサンドボックスでは、さらに多くの作業が必要です。
- 合成 SID がまだ作成されていない場合は、それを作成する
- オンラインおよびオフラインのサンドボックスユーザーがまだ作成されていない場合は、それらを作成する
- 新しく作成されたユーザーの認証情報をローカルに保存し、Windows データ保護 API (DPAPI) を使用して暗号化し、サンドボックスユーザーが実際に読み取ることのできない場所に格納する
- CodexSandboxOffline ユーザーのすべてのアウトバウンドネットワークアクセスをブロックするファイアウォールルールを作成する。または、既に存在する場合は、それらが正しいことを検証する
セットアップ段階には追加の複雑さがあります。Codex のサンドボックスは、実際の Windows ユーザーと同等の読み取り権限を持つことが期待されています。制限されたトークンの主 SID が Windows ユーザーであった非昇格サンドボックスでは、これが実現されていました。しかし、主が新しい CodexSandbox ユーザーになった場合、これは無償で得られるものではありません。Windows 上の多くの関連ディレクトリは、「認証済みユーザー」に対して読み取り/実行権限を付与します。顕著な例として、ユーザーのプロファイルディレクトリがあります。デフォルトでは、Windows ユーザーは他の Windows ユーザーのプロファイルディレクトリを読み取ることができないため、多くのシナリオで単純なファイル読み取りさえ失敗してしまいます。
これに対処するため、サンドボックスセットアッププロセスにもう一つの層を追加しました。それは、そのような ACL (アクセス制御リスト) が既に存在しない場合に、サンドボックスユーザーに対して *読み取り* 権限の ACL を付与するためのものです。例えば、以下のような一般的に使用される Windows ディレクトリに対してです:
- C:\Users\<real-user>
- C:\Windows\
- C:\Program Files\
- C:\Program Files (x86)\
- C:\ProgramData\
このディレクトリリストはベストエフォート型であり、各ディレクトリに ACL(アクセス制御リスト)を適用するにはコストがかかる可能性があるため、このロジックは非同期で実行されます。これにより、ユーザーにとってブロッキングとなるサンドボックス設定ステップが、完了を待たずに進行できるようになります。
設定ロジックを独自のバイナリにカプセル化した主な理由は、必要な場合のみ UAC(ユーザーアカウント制御)の境界を越えるためです。しかし、より深い理由としてはアーキテクチャ的なものです。サンドボックスの設定作業は codex.exe の役割とは根本的に異なります。設定ロジックを専用のバイナリに保つことで、codex.exe を通常の権限昇格なしのハーンネスとして維持でき、Windows 固有の設定機構が他のプラットフォームでも codex.exe を肥大化させるのを防ぎました。また、長時間かかる設定作業をメインプロセスのライフサイクルから切り離し、サンドボックスが必要とする異なる設定パスを一元管理できる場所も確保できました。
Windows のユーザーおよびトークンのログイン境界の仕組み上、権限昇格なしのサンドボックスで行っていたような制限付きトークンの作成とその下でのプロセス起動を継続することはできませんでした。実際には別の Windows ユーザーとしてコマンドを実行するために、最初のアイデアは以下のフローでした:
- codex.exe は実際の Windows ユーザーとして実行されます。その後、Codex:Calls LogonUserW(...) を呼び出してサンドボックスユーザーのトークンを取得します。
- そのサンドボックスユーザーのトークンに対して CreateRestrictedToken(...) を呼び出します。
- 作成された制限付きのサンドボックスユーザートークンを使用して CreateProcessAsUserW(...) を呼び出し、最終的な子プロセスを起動します。
実際には、CreateProcessAsUserW(...) の権限壁のために、望ましいフローは機能しませんでした。つまり、codex.exe はサンドボックスユーザー用の制限付きトークンを作成できますが、境界のリアルユーザー側からそのトークンを使用して子プロセスを確実に起動することはできませんでした。必要だったのは、すでにサンドボックスユーザーとして実行されているプロセスでした。これにより、制限ステップと最終的なスパンが、境界のリアルユーザー側ではなく、サンドボックスユーザー側で発生するようになります。
この要件から、制限付きトークンを生成し、要求されたコマンドを起動することだけを目的とした新しいバイナリである codex-command-runner.exe が生まれました。codex.exe に「リアルユーザー→サンドボックスユーザー→制限付きトークン→子プロセス」という一連のフロー全体を行わせるのではなく、このフローを 2 つの部分に分割しました。
Part 1
- codex.exe は CreateProcessWithLogonW(...) を呼び出して、codex-command-runner.exe をサンドボックスユーザーとして起動します。ただし、まだ制限付きトークンは使用しません。
Part 2
- ランナー内部では、OpenProcessToken(GetCurrentProcess(), ...) がランナー自身のトークンを開きます。このトークンはすでにサンドボックスユーザーに属しています。
- ランナーは GetTokenInformation(...) を呼び出してサンドボックスログオン SID を抽出し、CreateRestrictedToken(...) を呼び出して最終的な制限付きトークンを構築します。
- さらにランナー内部で、その制限付きトークンを使用して CreateProcessAsUserW(...) を呼び出し、実際の子プロセスを起動します。
アルベルト・アインシュタインは「すべては可能な限りシンプルに作られるべきだが、それ以上単純にしてはいけない」と言いました。その精神に基づき、私たちの設計は各問題を十分に解決しました。最終的なアーキテクチャには、これまでにご紹介した 4 つのレイヤーがあります:
- codex.exe そのもの
- すべての権限昇格を伴うセットアップ関連作業を処理するための codex-windows-sandbox-setup.exe
- 制限付きトークンを持つコマンドを実行するための codex-command-runner.exe
- 子プロセス
このプロジェクトに初めて取り組んだ際、どこに行き着くのかという確固たる見通しは持っていませんでした。私のアプローチは、Codex とオペレーティングシステムの境界においてサンドボックス機能を計測することから始めました。このアプローチは、MacOS および Linux 上で Codex のサンドボックスが実装されている方法と密接に一致しています。
Windows が提供する特定のツールについてより多くを学び、セキュリティと使いやすさのバランスを取るための数十回の意思決定を経て、システムは現在の形へと成長しました。すなわち、複数のバイナリ、カスタムユーザー、ファイアウォールルール、権限昇格を伴うセットアップステップ、非同期プロセスなどです。
これは特にシンプルなシステムではありませんが、複雑さの各要素は必要性に基づいて追加されました。それは、安全であると同時に、可能な限りユーザーの邪魔にならないサンドボックスを構築するためです。
安全性と実際の有用性のバランス
Windows 上の Codex ユーザーにとって良好なユーザー体験を提供することに注力し、私たちの目標は、有用性を犠牲にすることなく安全なものを作ることでした。Codex を使用する意義そのものは、エージェントがあなたの絶え間ない注意を必要とせずに作業を行えるようにすることにあります。
このプロジェクトから得た最大の教訓の一つは、Windows が「安全な自律型コーディングエージェント」に明確に対応する単一のプリミティブを提供していなかったことです。私たちはいくつかのツールと概念を組み合わせて、一貫性のあるものを構築しました。初期のアイデアの中には行き止まりとなったものもありました。最終的な設計は、それぞれが問題の一部を解決していた以前のプロトタイプを組み合わせるハイブリッド型でした。
もう一つの教訓は、コーディングエージェントのセキュリティは、より古典的なアプリケーションセキュリティとは異なる性質を持つということです。Codex は実際の開発者のワークフローで機能する必要があります。エンジニアリング作業では、アジェンシーな負荷との互換性と、実際の強制力とのバランスを取ることが中心でした。この緊張関係が最終設計におけるトレードオフを形作りました。
Codex のサンドボックスを実際に確認してみたいですか?こちらでお試しください。
原文を表示
When I joined the Codex engineering team in September 2025, Codex for Windows didn’t have a sandbox implementation meaning that Windows users were forced to choose between two subpar options when using OpenAI's coding agents:
- Approving nearly every command (even reads) that a coding agent wanted to run, which is inefficient and pesky. A major benefit of using Codex is that you don’t have to do all the tedious work yourself.
- Enabling Full Access mode: letting Codex run all commands without approval or restrictions, which removes friction at the expense of oversight.
Codex, our coding agent, runs on developer laptops—whether that's through the CLI, the IDE extension, or the desktop app. It manages a conversation between a human at a keyboard and a model running in the cloud to handle inference.
Codex runs with the permissions of a real user by default, meaning it can do everything the user can do. This is powerful and potentially dangerous. The coding model may tell the harness to run commands locally, from running tests to reading or editing a file to creating a Git branch, so Codex's default mode attempts to find the right balance between effectiveness and safety. This default mode allows Codex to read files almost anywhere and write files within your workspace (i.e., the directory where you're running Codex), with no internet access unless you specify you want it. To achieve this automatic constraint of writing files and accessing the network within safe bounds, Codex needs a sandbox environment that actually enforces these constraints.
A *sandbox* is a constrained execution environment. When a developer uses Codex, their computer's operating system launches a command with reduced permissions, and those constraints propagate down the process tree. Every Codex command is sandboxed from the start, and every descendant process stays inside the same boundary.
Codex needs isolation features enforced by the computer's operating system to implement an effective sandbox. Some operating systems provide utilities that do this well (e.g., Seatbelt on MacOs, seccomp or bubblewrap on Linux); however, Windows doesn't currently provide this type of capability out of the box.
To make Codex just as safe and delightful to use on Windows as it already is everywhere else, we needed to implement our own sandbox.
Where existing Windows tools fell short
Windows offers some tools and primitives for isolation. While none of them quite met our requirements, we looked at a number of potential solutions—namely, AppContainer, Windows Sandbox, and Mandatory Integrity Control labeling.
AppContainer
- What: AppContainer is the native Windows sandbox, a capability-based isolation model built for apps that know, up front, exactly what they need to access.
- Why: Appealing because it offers a real OS boundary instead of best-effort restrictions.
- Why not: Codex is not one tightly scoped app. It drives open-ended developer workflows: shells, Git, Python, package managers, build tools, and whatever other binaries the agent decides it needs. In practice, that made AppContainer the wrong shape for the problem. It was strong isolation, but for a much narrower class of workloads than “let an agent operate like a developer.”
Windows Sandbox
- What: Windows Sandbox is Microsoft’s disposable lightweight VM. You get a fresh Windows desktop with a strong isolation boundary, and whatever you do inside it disappears when the session ends.
- Why: Interesting for obvious reasons—far more compatible with arbitrary software than AppContainer, and from a security perspective it's a much stronger box.
- Why not: Codex needs to act directly on the user’s actual checkout, tools, and environment, not inside a separate throwaway desktop that would need setup and host/guest bridging. It also had a fundamental product problem: Windows Sandbox isn't even available on Windows Home SKUs.
Mandatory Integrity Control (MIC) integrity labeling
- What: Windows has a concept called “integrity levels,” such as low, medium, and high, that determine how much the system trusts objects and processes. The basic rule is that a lower-integrity process cannot write to an object with a higher integrity level, even if the normal ACL would otherwise allow it. For example, a low-integrity process is treated as less trusted, so Windows blocks it from writing to normal medium-integrity objects, unless those objects are explicitly relabeled to allow it.
- Why: MIC looked elegant on paper—run Codex at low integrity, relabel the writable roots as low integrity, and let Windows enforce no-writes everywhere else. That would've given us a non-admin path with a real OS mechanism behind it.
- Why not: Like ACLs, integrity labels modify the real host filesystem, and in this case the semantic change is especially broad. Marking a workspace as low integrity does not just mean “Codex can write here.” It means low-integrity processes in general can write there. On a real developer machine, that turns the user’s actual checkout into a low-integrity sink for the host, which is much riskier than granting carefully targeted ACLs to one sandbox design. Even if medium-integrity developer tools continue to work, the underlying trust model of the workspace has changed in a way that's hard to contain and harder to justify.
Having evaluated all of the options as non-starters, we started designing our own solution to bring a good Codex experience to Windows users.
The first prototype: the "unelevated sandbox"
Our first working prototype used a combination of Windows concepts and tools to implement the isolation we needed. From the beginning, one goal was to make this work without requiring *elevation*, meaning that Codex would not need to prompt the user for administrator privileges just to set up or run the sandbox. That meant figuring out how to put reasonable limits on two things: file writes and network access.
If we didn't limit file writes at all, we'd have a safety issue. If we limited file writes too much, the sandbox would hurt user productivity, needing to ask for constant approval. To solve this problem, we relied on two important Windows building blocks: SIDs and write-restricted tokens.
A SID, or security identifier, is the identity Windows ties to permissions. Each user has a SID, groups have SIDs, and even a single login session gets its own SID. For example, a current logged-in session might have a SID like S-1-5-5-X-Y. The SID assigned to the local administrators group might be S-1-5-32-544.
Windows also lets you create synthetic SIDs that don't correspond to a real user but can still appear in ACLs (access control lists), which define who can read/write/execute specific files or directories. That makes SIDs a useful primitive for our sandbox: we can create SIDs exclusively for the Codex sandbox to use, without interfering with anything else on the machine.
Process tokens are security objects in Windows that define identity and privileges for a running process. They determine what actions a process can perform. A *write-restricted token* is a particular type of process token that makes Windows perform an additional access check on write operations.
In order for a write to succeed, two checks must pass:
- The normal user identity (the token “owner”) must be allowed to do it
- At least one SID in the token’s restricted SID list must also be granted access
In practice, these checks let us use ACLs to define exactly where the sandbox could modify the filesystem, which offered the granularity we needed around write operations.
With SIDs and write-restricted tokens, our unelevated sandbox worked like this:
- The sandbox setup created a synthetic SID called sandbox-write.
- The sandbox-write SID was granted write, execute, and delete access toThe current working directory
- Any additional writable_roots configured in config.toml.
- The sandbox setup explicitly denied that same SID write access to “read-only within writable” locations such as:<cwd>/.git
- <cwd>/.codex
- <cwd>/.agents
- Codex launched commands under a write-restricted token whose restricted SID list includes Everyone, the current logged in session SID, and the sandbox-write synthetic SID.
This flow effectively solved limiting file writes and seemed promising. Now we needed a solution for limiting the sandbox's network access.
Limiting network access is an important part of the sandbox; without it, malicious code could exfiltrate data from the machine up to the internet. Because we wanted to avoid an elevation requirement, we had limited options to strongly block network traffic. The tools we wanted to use, like Windows Firewall, generally could not be installed without admin permissions.
Without Windows Firewall as an option, we limited what we could control. We tried to make the child environment fail-closed for the kinds of networked tools developers actually use, so that Git commands, package installers, etc., would fail in the sandbox and the user would have to approve any internet-facing operations. The idea was to poison the obvious escape hatches: send proxy-aware traffic to a dead endpoint, make Git’s HTTP(S) transport do the same, and make Git over SSH fail immediately. On top of that, we prepended a small denybin directory to PATH and reordered PATHEXT so stub SSH and SCP scripts would resolve before the real binaries.
For example, here are some of the specific environment overrides we used to limit network access:
- 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
That caught a lot of normal tool-driven traffic, but it was still only advisory. A process could ignore the environment, bypass PATH, or just open sockets directly—too risky.
As with any interesting software implementation, the first prototype had some pros and cons. While it got the job done with only a few standard Windows capabilities, allowed for very explicit and granular filesystem writes, and ran unelevated—cutting the need for users to accept excessive elevation prompts or be admins on their local machine—it had some real drawbacks, some of which disqualified it from becoming our final design:
- Speed of setup: Applying workspace ACLs can be expensive depending on the topology of the workspace directory.
- Footprint: We applied real ACLs to the developer’s system, although the footprint is not particularly invasive because all the applied ACLs pertain to a custom-created synthetic SID that is used only by the sandbox.
- Difficult-to-change semantics: The reliance on ACLs for file-based restrictions means it's expensive and complex to change sandbox semantics. Whereas on macOS, we can dynamically change how we generate the .sbpl file used to configure Seatbelt, the Windows sandbox could require a slow and intense operation to adjust ACLs.
- Network protection is weak. As mentioned before, it was “advisory,” would definitely be circumvented by some programs that implemented their own networking stack, and wasn't designed to hold up to adversarial code.
The first three issues are inherent to a custom sandbox implementation that's flexible enough for agentic flows. The network suppression story was different, though.
In addition to a malicious agent being able to easily circumvent the environment-based network suppression, plenty of good-intentioned code/binaries would also circumvent it simply if they didn’t honor the environment proxy variables, or if they implemented their own socket-based network code. We felt that this aspect was enough to consider investing in a better sandbox mode.
To gain better network suppression, we wanted to use Windows Firewall, which allows us to block outbound network traffic for users or programs. Unfortunately, we couldn't effectively create a functional firewall rule that applied only to the commands spawned by the Codex harness for a few reasons:
- Windows doesn't allow matching a firewall rule to the non-principal identity of a restricted token. This means we couldn't apply a firewall rule to “any token that includes our synthetic SID in its restricted SID list."
- While we could create a firewall rule that matches a specific binary, that only allows us to limit networking for codex.exe itself. It wouldn't apply to the processes that the agent spawns on behalf of the user, like Git or Python processes.
- Other firewall match dimensions were the wrong shape, too. User-scoped rules still matched the real Windows user in the unelevated design, not just the restricted child. Program-path rules were too coarse: they could block codex.exe or python.exe generally, but not this one sandboxed invocation of python.exe. Port- or address-based rules were also the wrong policy entirely. For instance, we didn't want to block port 443; we wanted to block arbitrary outbound access for this specific restricted process tree.
To apply a firewall rule specifically to our sandboxed commands, we needed to run them as a separate principal, not as the “real” user. This approach led us down a new path, one in which we relaxed our “no elevation” constraint.
The redesign: the "elevated sandbox"
The next iteration of the sandbox, which is our current implementation, requires elevated admin permissions at setup time. I therefore refer to it as “the elevated sandbox.” At the boundary where Codex spawns a command on the system, the elevated sandbox looks like the unelevated one. It still runs child processes under a restricted token—similarly a write_restricted token with the same restricted SID list of [Everyone, Logon, Synthetic]—however, the principal of this token is no longer the actual Windows user but one of two local users created by Codex itself:
- CodexSandboxOffline (the one targeted by firewall rules)
- CodexSandboxOnline (the one not targeted by firewall rules)
This seemingly small detail actually has big implications for the sandbox, who can use it, and the complexity of its setup and runtime execution.
It’s visually similar to the unelevated prototype, with the introduction of firewall rules and a dedicated Windows user, which actually runs the commands. (However, the introduction of these new concepts, means that there is more setup work to do before the sandbox can start running and protecting commands.)
The unelevated sandbox design had a simple setup step, but it was relatively small:
- Create a synthetic SID if needed
- Apply ACLs for the sandbox-write synthetic SID
The elevated sandbox, however, has more to do.
- Create a synthetic SID, if not already created
- Create the online and offline sandbox users, if not already created
- Store the newly-created users’ credentials locally and encrypt using the Windows Data Protection API (DPAPI) in a place where the sandbox users cannot actually read
- Create firewall rules that block all outbound network access for the CodexSandboxOffline user or, if they already exist, validate they're correct
There's an additional wrinkle in the setup stage. Codex’s sandbox is expected to have read access equivalent to the actual Windows user. In the unelevated sandbox, where the restricted token’s principal SID was the Windows user, this was achieved. However, that doesn't come for free when the principal becomes a new CodexSandbox user. Many relevant directories on Windows will grant read/execute permissions to “Authenticated Users”. One notable example is the user’s profile directory. By default, Windows users cannot read the profile directories of other Windows users, so even simple file reads in many scenarios would fail.
To address this, we added another layer to the sandbox setup process—one for granting *read* ACLs to the sandbox users where such ACLs might not already exist. For example, to some commonly used Windows directories:
- C:\Users\<real-user>
- C:\Windows\
- C:\Program Files\
- C:\Program Files (x86)\
- C:\ProgramData\
Because this list of directories is best-effort and installing ACLs on each one can be quite expensive, we run this logic asynchronously so the sandbox setup step, which is blocking to users, doesn't have to wait for them to complete.
We encapsulated the setup logic in its own binary partly to cross the UAC boundary only when needed. But the deeper reason was architectural: sandbox setup has a fundamentally different job from codex.exe. Keeping the sandbox setup logic in a dedicated binary let codex.exe stay a normal, unelevated harness; kept the Windows-only setup machinery from bloating codex.exe on other platforms; decoupled longer-running setup work from the lifetime of the main process; and gave us one place to handle the different setup paths the sandbox needed.
Because of how Windows user and token login boundaries work, we couldn't continue to create a restricted token and spawn a process under it the way we could with the unelevated sandbox. To actually spawn commands as a different Windows user, our first idea was the following flow:
- codex.exe runs as the real Windows user. Then, in a sequence, Codex:Calls LogonUserW(...) for the sandbox user.
- Calls CreateRestrictedToken(...) on that sandbox-user token.
- Using that restricted sandbox-user token, calls CreateProcessAsUserW(...) to launch the final child.
In practice, that desired flow didn't work because of a privilege wall at CreateProcessAsUserW(...). This means codex.exe could create a restricted token for the sandbox user, but it couldn't reliably launch a child with that token from the real-user side of the boundary. We needed a process that was already running as the sandbox user—this would let the restriction step and final spawn happen on the sandbox-user side of the boundary instead of the real-user side.
That requirement led to codex-command-runner.exe, a new binary whose only job is to mint a restricted token and spawn the requested command. Instead of asking codex.exe to do the entire flow itself (real user → sandbox user → restricted token → child process), we split the flow in two:
Part 1
- codex.exe calls CreateProcessWithLogonW(...) to launch codex-command-runner.exe as the sandbox user, without using a restricted token yet.
Part 2
- Inside the runner, OpenProcessToken(GetCurrentProcess(), ...) opens the runner’s own token, which already belongs to the sandbox user.
- The runner calls GetTokenInformation(...) to extract the sandbox logon SID, then CreateRestrictedToken(...) to build the final restricted token.
- Still inside the runner, it calls CreateProcessAsUserW(...) with that restricted token to launch the real child.
Albert Einstein said, “Everything should be made as simple as possible, but no simpler.” In that spirit, our design adequately solved each problem. The final architecture has the four layers we have previously covered:
- codex.exe itself
- codex-windows-sandbox-setup.exe for handling all elevated setup related work
- codex-command-runner.exe for running restricted token commands
- The child process
When I first approached this project, I did not have a strong sense of where it would wind up. My approach was to start by instrumenting the sandboxing capability in the boundary between Codex and the operating system. This approach closely matches how Codex’s sandbox is implemented on MacOs and Linux.
As I learned more about the specific tools that Windows provides, and through dozens of decisions balancing security and ease of use, the system grew to its current form—multiple binaries, custom users, firewall rules, an elevated setup step, asynchronous processes, and more.
It’s not a particularly simple system, but each piece of complexity was added out of necessity, to build a sandbox that is both safe and, as much as possible, not in the user's way.
Balancing safety with actual usefulness
Working to deliver a good user experience for Codex users on Windows, our goal was to make something safe that didn't compromise on usefulness—the whole point of using Codex is to have agents be able to do work without your constant attention.
One of the biggest lessons from this project was that Windows did not hand us one primitive that cleanly maps to “safe autonomous coding agent.” We composed several tools and concepts to build something coherent. Some early ideas were dead ends. The final design was a hybrid of earlier prototypes that each solved part of the problem.
The other lesson was that security for a coding agent is a different beast than more classic application security. Codex has to work for real developer workflows. The engineering work was about balancing compatibility with agentic workloads against real enforcement. That tension shaped tradeoffs in the final design.
Curious to see the Codex sandbox in action? Try it out.
関連記事
今日のまとめ
AI日報で今日の重要ニュースをまとめ読み