2025 年 9 月,David Wiesen 加入 Codex 团队的时候,Windows 上的 Codex 还没有沙箱这回事。

那时 Windows 用户只有两个选择。第一,几乎每条命令都点一次批准,连读文件也要问,用起来像在给自己的电脑做审核。第二,直接开 Full Access,把所有限制关掉,爽是爽,等于把笔记本交给一个会自己敲命令的程序。

中间那条路才是大多数人想要的:能读文件,能改当前项目里的文件,不能随手把系统改了,也不能偷偷联网。这条路不难描述,难的是谁来执行。它必须由操作系统强制,而不是靠模型自觉。

macOS 有 Seatbelt,Linux 有 seccomp 和 bubblewrap,把这两件事做得挺顺手。Windows 没有对应的现成东西。所以 Codex 团队自己造了一个。

沙箱约束的到底是什么#

沙箱是一个被削了权限的执行环境。开发者敲下命令时,操作系统用的是降权后的身份启动它,而这个约束会沿着进程树往下传:每个 Codex 执行的命令从一开始就在盒子里,它自己拉起来的子进程也在同一个盒子里。

这里有个区分值得记牢。沙箱决定技术上能做什么,审批决定什么时候必须停下来问你。前者是操作系统层面的硬边界,后者是交互策略。很多人把两件事混着说,结果既高估了沙箱(以为它会问),也低估了沙箱(以为它只是提示)。

三个现成方案,形状都不对#

OpenAI 先看了 Windows 上现成的隔离能力:AppContainer、Windows Sandbox,以及 Mandatory Integrity Control 完整性标签。三个都是真东西,但都对不上 Codex 的需求。

AppContainer 是 Windows 原生的沙箱,一套能力模型,为那种事先就知道自己要访问什么的 app 设计。这个前提就挡住了 Codex。它不是一个小而明确的 app,它是一堆开放式工作流的驱动者:shell、Git、Python、包管理器、构建工具,以及模型当场决定要用的任何二进制。拿一个为窄场景设计的隔离机制去装一个开放式 agent,形状不对。

Windows Sandbox 是微软的一次性轻量虚拟机,进去是个全新的 Windows 桌面,隔离边界很硬,会话结束里面发生的一切就没了。问题也在这儿:一个每天要改本机仓库的人,用这个等于另开一台机器,还得想办法把成果搬回来。另外它只在专业版及以上的版本里提供,家庭版用户根本用不上。

MIC 纸面上最优雅。Windows 有完整性级别的概念,低、中、高,规则是低完整性进程不能写高完整性的对象,哪怕 ACL 允许。按理说可以这样干:让 Codex 在低完整性下跑,把可写的根目录标成低完整性,其他地方交给 Windows 挡。但这个方案的真实语义是,把一个工作区标成低完整性,不等于「Codex 能写这里」,而等于「所有低完整性进程都能写这里」。开发者真实的 checkout 目录就这样变成了宿主机上的低完整性落点,信任模型被改宽了,而且很难收回来。

三个方案的问题其实是同一个:它们动的是「机器上某个对象的信任级别」,而 Codex 要的是「这一棵进程树、这一个目录,而且能按需放松」。原来的问题不是隔离不够强,是形状不一样。

第一版:能管住写,拦不住网#

有了这个判断,团队开始用两块 Windows 积木搭:SID 和写受限令牌。

SID 是 Windows 用来记身份的东西。每个用户有 SID,每个组有 SID,连一次登录会话都有自己的 SID,比如管理员的组 SID 是 S-1-5-32-544。Windows 还允许创建不对应真实用户的合成 SID,这种 SID 照样能出现在 ACL 里。这就是关键:可以造一个只属于 Codex 沙箱的身份,不碰机器上其他任何东西。

他们造了一个叫 sandbox-write 的合成 SID,再用 ACL 精确地给它开权限:允许写工作区,把几个必须能动的地方留出例外,.git、.codex、.agents 这些目录按需要处理,而像 .aws 这种放云凭据的位置默认只读。

然后是一块更精巧的积木,写受限令牌。它是一种特殊的进程令牌,作用是让 Windows 对写操作多做一次检查。Codex 启动命令时用的受限 SID 列表是 [Everyone, 当前登录会话的 SID, sandbox-write]。也就是说,一个进程只有在 ACL 上明确给了 sandbox-write 权限的地方才写得进去,「因为我是你本人所以能写」这条捷径被断掉了。

文件写入这块算是硬约束。网络那块当时不是。

因为不想让安装过程要求管理员权限,第一版动不了 Windows 防火墙。他们用的是「劝」的办法:把代理变量指向一个死活连不上的地址,让 Git 的 HTTP(S) 传输也走同样死掉的地址,让 Git over SSH 直接失败;再在 PATH 最前面塞一个叫 denybin 的目录,调整 PATHEXT 的顺序,让伪造的 ssh 和 scp 在真身之前被找到。

结果很明显:这只能拦住正常工具的正常行为。一个进程可以完全不理环境变量,可以绕过 PATH,也可以直接开 socket。作者自己的评价是,这些手段仍然只是建议性的,太不安全。

第二版:换来真隔离,代价是要提权#

要做真正的网络隔离,就得用 Windows 防火墙,它是按用户或按程序封出站流量的。但「只封 Codex 这棵树」这件事,Windows 恰好做不到:

  • 防火墙规则没法匹配到受限令牌里的非主身份,所以不能写一条「凡是受限 SID 列表里带我们这个合成 SID 的令牌都封」的规则。
  • 按二进制匹配只能盖住 codex.exe 自己,盖不住它替用户拉起来的 Git、Python 这些进程。
  • 按用户匹配,在旧方案里会匹配到真实用户,不是那个受限子进程。
  • 按程序路径太粗,能封掉 python.exe 这个程序,封不掉「这一次被沙箱包住的 python.exe」。
  • 按端口或地址是完全错的策略。他们要封的不是 443,而是这一棵受限进程树的任意出站访问。

要拿到一条只作用在沙箱命令上的防火墙规则,那些命令必须以另外的主体身份运行,不能是「真实用户」。这就意味着放弃不提权的坚持。于是有了第二版,作者的叫法是 elevated sandbox。翻成中文就是提权沙箱,但要留意,提权只发生在安装的时候,跑命令的时候反而是降权的。

在命令被启动的那个边界上,提权版本看起来和不提权版本一样:还是写受限令牌,还是同一套受限 SID 列表 [Everyone, Logon, Synthetic]。变的只有一件事:令牌的主体不再是真实的 Windows 用户,而是 Codex 自己创建的两个本地用户。一个叫 CodexSandboxOffline,防火墙规则针对它;另一个叫 CodexSandboxOnline,不针对它。命名很直白,要不要联网,就看你归哪个用户管。

这个改动看着小,影响很大。它带来一整套必须提前做好的准备:创建合成 SID,创建在线和离线的两个沙箱用户,把这两个用户的凭据在本机加密保存,用的是 Windows Data Protection API,而且存在沙箱用户自己读不到的位置,再创建阻止 CodexSandboxOffline 全部出站流量的防火墙规则,如果规则已经存在就校验它是否正确。

代价也直接:安装时需要管理员权限。企业环境里这件事经常被策略挡住,官方文档因此保留了 unelevated 作为兜底。两版的定位很清楚,elevated 是首选,unelevated 是装不上时先让你能干活。代码仓库里也拆成了两个可执行文件,一个负责提权做环境准备,一个负责在受限身份下跑命令。企业管理员还能用 requirements.toml 限制员工只能使用哪一种,防止有人图省事往回落。

有个细节值得提前记住:提权版本在 ACL 上的铺开范围不小,而这一点后来变成了真实抱怨。

现实检验:上线之后发生了什么#

沙箱上线之后的故事,比文章本身更有意思。

2026 年 3 月,有人在 openai/codex 仓库提交了 issue #12343,环境是 Codex 0.101.0 加 Windows 11 专业版。他发现的不是普通 bug,而是 CodexSandboxOffline 和 CodexSandboxOnline 这两个用户,被授予了几乎整个用户 profile 目录树的访问权限。用他的话说,Codex 现在能读他电脑上差不多所有东西。

评论区的情绪不难理解。有人说这是严重的隐私问题,有人说改完以后没有容易的办法撤回,还有人抱怨相关改动几乎没有文档,IT 部门没法凭文档里那两句话做决策。OpenAI 的回复出现在 4 月 8 日:这件事暂时不会改,如果介意沙箱用户有宽泛的读权限,可以把配置改成 sandbox = "unelevated",然后手动删掉那些 ACL,提权沙箱不会再把它们加回去。

同一时期还有两条相关 issue,一条是新建文件夹时 ACL 设错,影响到 apply_patch 的正常工作,另一条是沙箱可能对所有人都可写的工作区路径自动加上 NTFS 拒绝条目。都是同一类问题的不同切面:沙箱为了能用,必须把权限铺得足够宽,铺宽之后的副作用,得有人来承担。

另一件更锋利的事发生得更早。安全公司 Cymulate 在 2026 年 2 月提交了一份报告,标题叫 The Pipe That Trusted Everyone。他们发现 Codex 每次执行命令都要开三个命名管道,用来传 stdin、stdout、stderr,而这些管道的安全描述符把完全控制权给了 Everyone,并且它们是单实例的。

这在共享机器上意味着什么,很清楚。构建服务器、跳板机、三个人下午都要 RDP 上去的那台开发机,只要有一个非特权账号,他就可以守在管道旁边,抢在真正的沙箱 runner 之前占住它,然后以 runner 的口气跟 AI 说话,注入一条假的沙箱错误,最后把受害者的 OpenAI 凭据拿走。整个过程不需要内存破坏,也不需要什么新奇的漏洞类型,靠的就是一条全世界都可写的命名管道。这类错误做法,业界已经教了差不多二十年。

披露时间线是这样的:2 月 18 日通过 Bugcrowd 提交完整报告和端到端 PoC,2 月 24 日分派给 OpenAI,4 月 24 日 OpenAI 修复并确认跨用户接管路径已经堵上。修复分两步,一是把 runner 的管道传输收到沙箱用户作用域内,二是加上客户端身份校验。

这两件事放在一起,我的判断是:别把沙箱当成一个「做完就安全」的功能。它是一块新长出来的操作系统集成,而操作系统集成的成熟度是按年算的,不是按发布节奏算的。ACL 铺太广和管道曾经信任所有人,是同一个问题的两端。你要让 agent 能读仓库、能拉起任何工具链,安全边界就必须画在比「一个 app」更宽、比「整个用户」更窄的某个位置上。这个位置没有现成的标准答案,画歪一点,代价立刻出现在真实用户的机器上。

在 Windows 上用 Codex,几条实际建议#

分清沙箱和审批这两条线。默认设置(能读、只能写工作区、不能联网)已经比 Full Access 好太多,但它是「有边界」,不是「没风险」。

优先用 elevated。装不上是常态,企业策略挡一下很常见,那就退到 unelevated,同时知道它更弱:网络隔离是环境层面的,不是防火墙层面的。

网络默认关着是好事,别嫌烦。一个本地命令能搞坏东西,一个带网络的本地命令还能把东西带出去。

如果在多人共用的机器上跑 agent,格外小心。命名管道那类问题就发生在共享环境里,受害者往往什么都没做错。构建机、跳板机、共享开发机上不适合长期挂一个权限不低的 agent。

需要读工作区之外的目录,先加最小范围的读权限,而不是直接切 Full Access。真出问题的时候,日志在沙箱目录下的 sandbox.log 里。

最后一句#

Codex 在 Windows 上这件事,值得记的不是模型又强了多少,而是一个产品值不值得信任,取决于它敢不敢让 agent 自己动手。这个「敢」不来自提示词写得多细,来自操作系统层面的那几条规则,以及团队愿不愿意为了这几条规则接受提权、接受 ACL 铺开、接受后续被用户和安全研究者追着改。

沙箱不酷,但它是 agent 能在你机器上跑起来的前提。

参考来源#

  • OpenAI, Building a safe, effective sandbox to enable Codex on Windows(David Wiesen,2026 年 5 月 13 日):https://openai.com/index/building-codex-windows-sandbox/
  • Codex 官方文档,Windows sandbox 与 sandboxing 概念:https://learn.chatgpt.com/docs/windows/windows-sandbox
  • openai/codex 仓库 issue #12343,关于 CodexSandboxOffline / CodexSandboxOnline 的 ACL 范围:https://github.com/openai/codex/issues/12343
  • Cymulate Research Labs, The Pipe That Trusted Everyone(Codex CLI Windows 沙箱命名管道问题):https://cymulate.com/blog/openai-codex-cli-named-pipe-vulnerability/