OpenAI が構築した Codex Windows サンドボックスの内幕(19 分読)
OpenAI は、Windows に標準搭載された分離機能の不足を補うため、独自のコデックスサンドボックスを実装し、開発者による安全かつ効率的なコーディングエージェント利用を可能にした。
キーポイント
Windows における既存の課題と二律背反
Codex は Windows で実行時、コマンド承認の手間(非効率)か、フルアクセス権限のリスク(安全性欠如)という二つの不十分な選択肢しか提供されていなかった。
OS 固有機能の限界と独自実装の必要性
macOS や Linux では Seatbelt や seccomp などの強力な分離ツールが存在するが、Windows には同様の標準機能が不足しており、OpenAI は独自のサンドボックスを実装せざるを得なかった。
サンドボックスによる権限制御の実現
実装された環境では、OS がプロセスツリー全体に制約を伝播させ、ファイルの書き込み範囲やネットワークアクセスを厳格に制限することで、安全性と有用性のバランスを実現した。
開発者体験の向上
この独自実装により、Windows ユーザーも他プラットフォームと同様に、頻繁な承認操作なしに安全にコーディングエージェントを利用できるようになった。
影響分析・編集コメントを表示
影響分析
この記事は、AI エージェントが実社会のインフラ(OS)とどのように統合されるかという重要な課題を浮き彫りにしており、特に Windows 環境におけるセキュリティ設計の難しさを示しています。OpenAI が独自にサンドボックスを実装したことは、大規模言語モデルの実用化において、プラットフォーム固有の制約を克服する技術的決断の重要性を示唆しています。今後は、他の企業も同様の課題に直面し、OS 依存しない安全な実行環境の標準化や、OS ベンダーとの連携が加速すると予想されます。
編集コメント
OS のセキュリティ機能の差異が、AI ツールの実用性に直結する好例であり、Windows ユーザーにとって待望のアップデートと言えます。技術的な背景を解説しているため、開発者やセキュリティ担当者にも参考になる内容です。
2025 年 9 月に Codex エンジニアリングチームに参加した際、Windows 版 Codex にはサンドボックスの実装がありませんでした。つまり、Windows ユーザーは OpenAI のコーディングエージェントを利用する際に、以下の 2 つのいずれかという劣った選択肢を迫られていました。
- コード実行をほぼすべて承認すること(読み取りコマンドを含む)。これは非効率的で煩わしいものです。Codex を利用する大きな利点の一つは、すべての面倒な作業を手動で行う必要がないことです。
- フルアクセスモードを有効にすること:Codex に承認や制限なくすべてのコマンドを実行させること。これにより摩擦は減りますが、その見返りに監視機能が失われます。
Codex は、開発者のノートパソコン上で動作するコーディングエージェントです(CLI、IDE 拡張機能、またはデスクトップアプリを通じて利用可能)。これは、キーボードを操作する人間とクラウドで推論を実行するモデルとの間の対話を管理します。
Codex はデフォルトで実在するユーザーの権限で実行されるため、ユーザーができることは何でも実行可能です。これは強力である一方で潜在的に危険でもあります。コーディングモデルは、テストの実行からファイルの読み書き、Git ブランチの作成に至るまで、ハーン(harness)に対してローカルでコマンドを実行させる可能性があります。そのため、Codex のデフォルトモードでは、有効性と安全性の適切なバランスを見つけるよう努めています。このデフォルトモードにより、Codex はほぼあらゆる場所からファイルを読み取り、実行中のワークスペース(つまり Codex を実行しているディレクトリ)内でのみファイルを書き込むことができます。ただし、ユーザーが明示的にインターネットアクセスを希望しない限り、ネットワークへの接続は行われません。
これらの安全な範囲内でファイルの書き込みやネットワークへのアクセスを自動的に制限するために、Codex には実際にこれらの制約を強制するサンドボックス環境が必要です。
*サンドボックス*とは、実行環境が制限された状態のことです。開発者が Codex を使用すると、コンピューターのオペレーティングシステムは権限を減らしたコマンドを開始し、その制約はプロセスツリー全体に伝播します。Codex のすべてのコマンドは最初からサンドボックス化されており、すべての子プロセスも同じ境界内に留まります。
Codex が効果的なサンドボックスを実装するためには、コンピューターのオペレーティングシステムによって強制される分離機能が必要です。一部のオペレーティングシステムにはこれをうまく行うユーティリティが用意されています(例えば、macOS 上の Seatbelt、Linux 上の seccomp や bubblewrap など)。しかし、Windows には現在、このような機能が標準で提供されていません。
Codex を Windows でも他の場所と同じように安全かつ快適に利用できるようにするため、独自のサンドボックスを実装する必要がありました。
既存の Windows ツールの限界
Windows には隔離のためのいくつかのツールやプリミティブが用意されています。これらはいずれも私たちの要件を完全に満たすものではありませんでしたが、私たちは複数の潜在的な解決策を検討しました。具体的には、AppContainer、Windows Sandbox、および必須整合性制御ラベル(Mandatory Integrity Control labeling)です。
AppContainer
- 概要:AppContainer はネイティブの Windows サンドボックスであり、アプリが事前にアクセスするものを正確に把握している場合に最適化された、能力ベースの隔離モデルです。
- 利点:ベストエフォートの制限ではなく、実際の OS の境界を提供するため魅力的です。
- 欠点: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(Security Identifier:セキュリティ識別子)とは、Windows が権限に関連付けるアイデンティティのことです。各ユーザーには SID が割り当てられ、グループにも SID があり、単一のログインセッションでさえ独自の SID を持ちます。例えば、現在ログイン中のセッションは S-1-5-5-X-Y のような SID を持つ可能性があります。ローカル管理者グループに割り当てられた SID は S-1-5-32-544 です。
Windows では、実際のユーザーに対応していない合成 SID を作成することもでき、これらは ACL(アクセス制御リスト)に表示させることが可能です。ACL は、特定のファイルやディレクトリを誰が読み書き・実行できるかを定義するものです。この機能により、SID はサンドボックスにとって有用な基本要素となります。つまり、Codex のサンドボックス専用として SID を作成し、マシン上の他の何ものにも干渉することなく利用できます。
プロセストークンは、Windows におけるセキュリティオブジェクトであり、実行中のプロセスのアイデンティティと特権を定義します。これにより、プロセスが実行可能なアクションが決定されます。*書き込み制限付きトークン*は、特定の種類のプロセストークンであり、書き込み操作に対して Windows が追加のアクセスチェックを実行させるものです。
書き込みが成功するためには、2 つのチェックが両方とも通過する必要があります:
- 通常のユーザーアイデンティティ(トークンの「所有者」)がその操作を許可されていること
- トークン内の制限付き SID リストに含まれる少なくとも 1 つの SID もアクセス権限が付与されていること
実際には、これらのチェックにより、ACL を用いてサンドボックスがファイルシステムを改変できる場所を正確に定義することが可能となり、書き込み操作に関する必要な粒度を実現できました。
SID と書き込み制限付きトークンを用いることで、特権昇格を行わないサンドボックスは以下のように動作しました:
- サンドボックス設定により、合成 SID である sandbox-write が作成されました。
- この sandbox-write SID には、現在の作業ディレクトリに対する書き込み、実行、削除のアクセス権が付与されました。
- config.toml に追加で設定された writable_roots にも同様の権限が付与されました。
- サンドボックス設定では、<cwd>/.git、<cwd>/.codex、<cwd>/.agents など、「書き込み可能な領域内でも読み取り専用」とされる場所に対して、同じ SID による書き込みアクセスを明示的に拒否しました。
- Codex は、Everyone、現在ログイン中のセッションの SID、および sandbox-write という合成 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 を作成する
- サンドボックス用 write_restricted 合成 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 つの部分に分割しました。
パート 1
- codex.exe は CreateProcessWithLogonW(...) を呼び出して、codex-command-runner.exe をサンドボックスユーザーとして起動します。ただし、この時点ではまだ制限付きトークンは使用しません。
パート 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日報で今日の重要ニュースをまとめ読み