跳到正文
meng shao· @shao__meng · X·· 2026-05-14AI 评分50
AI 导读

OpenAI 为在 Windows 上实现 Codex 的“默认安全”体验,从免提权沙箱演进到提权沙箱。Windows 缺乏原生进程级约束,初期方案通过合成 SID 和 Write-Restricted Token 限制文件写入,但网络封锁只能依赖环境变量软拦截,无法强制生效。团队最终放弃免提权约束,转向创建独立本地用户(在线与离线沙箱用户),需一次性管理员权限安装并配置防火墙规则。通过引入 codex-command-runner.exe 作为中介,解决跨用户创建受限令牌进程的权限难题,形成四层架构,在保障安全的同时最小化对主流程的侵入。

正文 · 原文

OpenAI 给 Codex 在 Windows 造了一个沙箱,过程比想象中曲折 ...

来自 Codex 团队 David Wiesen 非常有深度的技术博客,推荐阅读!
https://openai.com/index/building-codex-windows-sandbox/

问题的起点:Windows 上的 Codex 没有沙箱
Codex 运行在开发者本地(CLI / IDE 扩展 / App),默认以当前用户身份执行命令——既能读写文件、跑测试、操作 Git,也意味着潜在风险。

macOS 有 Seatbelt,Linux 有 seccomp/bubblewrap,Windows 原生缺乏这种"按进程做强约束"的能力。结果 Windows 用户只能在两个糟糕方案中二选一:
· 每条命令都审批(甚至读操作),打断流畅性;
· 开启 Full Access,放弃所有约束。

团队的目标,是把 Codex 在 macOS/Linux 已有的"默认安全"体验搬到 Windows:只能在工作区内写、默认无网络访问,且全程不需要用户介入。

现成 Windows 方案为什么都不够用?
· AppContainer:是为"功能边界清晰的应用"设计的;Codex 要驱动 shell、Git、Python、构建工具等任意二进制,形状不对
· Windows Sandbox:它是隔离的"另一个桌面",无法直接作用于用户的真实仓库;且 Windows Home 版根本没有
· Mandatory Integrity Control:把工作区标成 Low,等于让所有 Low 进程都能写入,宿主信任模型被破坏,副作用太大

第一版原型:「免提权沙箱」(Unelevated Sandbox)
设计原则:不弹 UAC、不要求管理员。需要解决两件事:限制文件写入 + 限制网络。

1. 文件写入:靠 SID + Write-Restricted Token 真正落地
· 合成 SID:Windows 允许创建一个不绑定真实用户、却能出现在 ACL 中的身份。Codex 为此造了一个专属的 sandbox-write SID。
· Write-Restricted Token:一种特殊进程令牌,写操作要双重放行——token 的真实用户身份有权限;
token 的"受限 SID 列表"中至少一个 SID 也被授权。

把 sandbox-write SID 通过 ACL 授予:
· 当前工作目录
· config.toml 里配置的 writable_roots

并显式拒绝其写入 .git / .codex / .agents。
→ 这是真正的 OS 级写边界。

2. 网络访问:只能"劝退",无法强制
Windows Firewall 必须管理员权限,于是只能做环境层面的软封锁:
HTTPS_PROXY / ALL_PROXY / GIT_HTTPS_PROXY = http://127.0.0.1:9
GIT_SSH_COMMAND = cmd /c exit 1
外加在 PATH 前塞 denybin,让假的 ssh/scp 先被解析到。

效果:拦得住行为良好的工具;但凡自己实现网络栈、绕过 PATH、或直接开 socket 的程序——一律失效。仅是 advisory,挡不住对抗性代码。

改版关键:为什么必须接受"需要提权"
要让 Windows Firewall 真正生效,必须按"身份"匹配规则。但:
· 防火墙规则不能匹配 restricted token 中的合成 SID;
· 按 codex.exe 路径匹配,覆盖不到它派生的 Git/Python 等子进程;
· 按用户匹配又会误伤真实用户本人;
· 按端口/地址匹配是错的策略——目标不是封 443,而是封这一棵受限进程树的所有出站流量。

唯一的出路:让沙箱命令以"另一个 Windows 用户"的身份运行。这就必须放弃"免提权"约束。

最终方案:「提权沙箱」(Elevated Sandbox)

1. 引入两个本地用户
Codex 在安装时创建:
· CodexSandboxOffline —— 防火墙规则全封;
· CodexSandboxOnline —— 不被防火墙规则覆盖。
子进程依旧跑在带 [Everyone, Logon, Synthetic] 受限 SID 列表的 write-restricted token 下,但 token 的主体(principal)换成了沙箱用户,而不是真实用户。

5.2 一次性 setup 步骤(需要管理员)
· 创建合成 SID;
· 创建在线 / 离线沙箱用户;
· 凭据用 DPAPI 加密存储,沙箱用户自己读不到;
· 为 CodexSandboxOffline 创建"封禁所有出站"的防火墙规则;
· 给沙箱用户补 读 ACL——因为新用户默认读不到其他用户的 profile、C:\Users、C:\Program Files 等常用目录。这一步耗时,异步执行,不阻塞用户。

5.3 为什么需要 codex-command-runner.exe
直觉的流程是:
codex.exe → LogonUserW → CreateRestrictedToken → CreateProcessAsUserW(child)
但在 CreateProcessAsUserW 这一步存在特权墙:以"真实用户"身份是无法可靠地把进程以另一个用户的受限 token 拉起来的。

解法是把流程切成两段:
Part 1(在真实用户侧)
· codex.exe 用 CreateProcessWithLogonW 把 codex-command-runner.exe 以沙箱用户身份拉起(此时还不是受限 token)。
Part 2(已经在沙箱用户侧)
· runner 用 OpenProcessToken 拿到自己的 token;
· GetTokenInformation 取出 logon SID;
· CreateRestrictedToken 构造最终受限 token;
· CreateProcessAsUserW 拉起真正的子进程。

5.4 最终四层架构
· codex.exe —— 普通非提权的 harness;
· codex-windows-sandbox-setup.exe —— 一次性的提权安装;
· codex-command-runner.exe —— 在沙箱用户内造受限 token 并起子进程;
· child process —— 真正受约束的命令。

拆成独立二进制的好处:codex.exe 在其他平台不被 Windows 专属逻辑污染;UAC 边界只在必要时跨越;setup 的长耗时与主进程生命周期解耦。

引用Tibo@thsottiaux
We are continuing to invest in making agents work better on Windows. Highly recommend reading David's engineering post on our unique approach to windows sandboxing for Codex: https://openai.com/index/building-codex-windows-sandbox/
在 X 查看被引用的帖子

来源:meng shao · x.com