学 AI 产品 · 专业 AI 产品经理播客第 3 章 · T3 解剖 OpenAI Codex:把安全观写进类型系统 · EP 11
第 3 章 · EP 11

Windows:受限令牌、防火墙过滤器与两个专用系统用户

时长 14:16音色 云健 · 男声

同步字幕

章节导航(点击跳转)

0:00开场 · 同一个意图,三台机器,三种失败2:03
2:03换一个问题 · 没有进程视图,就按身份锁资源1:54
3:57三个标志 · 管得住写,管不住读和网络1:31
5:29两个专用账户 · 一个身份只能挂一套网络策略1:38
7:08WFP 的真相 · 说完规则,说残留1:28
8:36为什么默认关 · 对不上的集合,太贵的代价2:10
10:46三种愿意付的代价 · 一题三答1:32
12:18可带走的设计原则 · 六条1:56
解读全文

codex11 · Windows:受限令牌、防火墙过滤器与两个专用系统用户

来源:xueai.miyang.cn(小山学堂 · 洛小山《学 AI 产品,从入门到精通》)
本内容改编自小山学堂《学 AI 产品,从入门到精通》,为二次演绎配音版

本集定位

本集属于技术侧(S2)模块 T3「解剖 OpenAI Codex:把安全观写进类型系统」,取材自 1 节课:

  1. Windows:受限令牌、防火墙过滤器与两个专用系统用户

这节课回答的是跨平台 Agent 里最容易被自欺的一件事:

Windows 上没有 seatbelt,也没有 bubblewrap —— 那这里的「沙箱」到底是什么?

答案是三道关卡叠起来:令牌管写(第一关)、专用系统用户管你是谁(第二关)、WFP 按这个身份滤网络(第三关)。而且整套东西默认关着。

一句话概括:Windows 没有进程视图,隔离只能按身份锁资源。


一、能力地图

读完本集(听完音频),你应该能够:

能力具体表现
识别三种失败语义区分 Operation not permitted / Access is denied / Forbidden-or-Prompt 各自对应的隔离深度
解释隔离原语转向说清「主体能做什么」与「客体能被谁碰」两套原语,以及为何 Windows 走后者
复述三个标志DISABLE_MAX_PRIVILEGE / LUA_TOKEN / WRITE_RESTRICTED 各管什么
说出管辖边界管写,不管读和网络——以及 RestrictedToken 档为何如此
区分两档身份来源RestrictedToken 从当前用户裁;Elevated 先登专用账户再裁
解释为什么要两个账户一个 SID 只能挂一套网络策略 → 身份数必须覆盖策略组合数
复述 WFP 覆盖面出站阻断、代理端口白名单、12 条过滤器(ICMP / DNS 53 / DoT 853 / SMB)
说出运维风险持久标志 → 重启后仍在;仓库无拆除函数;WFP 失败非致命 / 防火墙失败致命
解释默认关AppContainer 对不上权限模型;Elevated 安装摩擦太贵
说清兜底位置沙箱不可用时,策略层把它判成 Forbidden 或 Prompt
横向对比三档答法说清不做 / 只做写限制 / 全做进去 三者的取舍依据

二、起点 · 同一个意图,三台机器,三种失败

机器Agent 想做的事结果
macOS(Seatbelt)读 ~/.ssh/id_rsaOperation not permitted
Windows(受限令牌/ACL)同上Access is denied
Windows(未启沙箱)同上根本走不到 ACL,execpolicy 先判 Forbidden / Prompt
危险之处:模型看到的下一句话不一样,它下一步的行为就会跟着变。

如果 Windows 上假装有和 Unix 一样的默认沙箱,模型就会按「自己被关着」去规划,实际命令却在裸跑。承诺了做不到的墙,模型和用户都会按那道墙做决策。

报错对应隔离深度
Operation not permitted有进程级沙箱
Access is denied只有身份 + ACL
Forbidden / Prompt没有后端,策略层兜底
这不是 API 差异,是语义差异。

三、思路一 · 没有进程视图,就按身份锁资源

3.1 三个平台的隔离原语

平台限制的是什么
macOS这个进程能对哪些路径做哪些操作
Linux这个进程能看见什么、能调哪些系统调用
Windows没有 namespace,没有 seccomp → 只能问:这个身份能碰哪些资源
两种隔离原语:「主体能做什么」 vs 「客体能被谁碰」。Windows 走的是后者。

3.2 令牌从哪来(关键差异)

档位起点
RestrictedToken从当前用户令牌往下裁 → 天然继承这个用户对工作区的可见性,只把写卡住
Elevated先建专用账户并登录,拿到那一份令牌,再从它往下裁
差距不是强度高低,而是「以谁的身份起步」——这决定了后面所有 ACL 对谁生效。

3.3 三个标志

标志作用
DISABLE_MAX_PRIVILEGE把不必要的特权削薄
LUA_TOKEN标准用户常规模式
WRITE_RESTRICTED写必须同时通过普通 ACL 与 restricting SID 的交叉检查

提示一 · 记住它不管什么

源码写得很清楚:WRITE_RESTRICTED 令牌上的 capability SID deny-read ACE 不参与读检查。

所以在 RestrictedToken 这一档:读工作区之外的文件会穿过,往外发网络也会穿过。

这一档能挡住的:模型误改仓库文件

这一档挡不住的:读出密钥、把东西往外传 —— 要拦必须换 Elevated。

3.4 别把 WFP 当成网络命名空间

Unix 网络隔离Windows WFP
做法给进程换一张网络命名空间视图进程还在主机网络栈上,只是这个 SID 发出的特定流量被内核滤掉
进程可见性看不见宿主网络仍能看见网卡、遇到解析失败、连别的端口

3.5 包装方式:自我调用

需要沙箱时,用自己的 exe + 隐藏参数 --run-as-windows-sandbox 当包装器 —— 与 Linux 改 arg0 是同一思路,不需要额外二进制。

硬事实前置:整套默认关着。开关没开时,平台沙箱返回空,后面不会再包任何包装器。

四、思路二 · 一个身份只能挂一套网络策略

4.1 为什么需要两个账户

WFP(Windows Filtering Platform)是内核包过滤框架。关键在于:

它和防火墙规则都按 SID 匹配,而一个 SID 只能对应一套网络策略。

同一个身份没法同时表示「完全没网」和「可走代理 / 自由出网」——身份与策略是一对一的。

4.2 两个专用系统用户

账户何时选用装什么
CodexSandboxOffline强制走代理,或网络策略未启用出站阻断 + 代理端口白名单 + 12 条 WFP(ICMP / DNS 53 / DoT 853 / SMB)
CodexSandboxOnline需要联网但仍要独立身份不装这套阻断

账户名在 setup.rs 中写死。

4.3 挂载点视角(重要)

把「读 SSH 私钥」留在原地,档位从 Elevated 降到 RestrictedToken:

第二关和第三关直接消失,那条读取一路穿过。

因为在令牌档根本没有那两个专用账户 —— 第三关连存在的条件都不满足。

同理,用户配置根默认挖空 .ssh 等目录,但这份排除列表只有在登录了专用账户之后才生效(它挂在那个账户的 ACL 上)。在令牌档,这不是「没配好」,而是那一层压根没有这个挂载点。

提示二 · 降级前先想清楚挂载点

从 Elevated 降到 RestrictedToken 时,不要以为只是「少几层保护」——第二关的 ACL 挖空和第三关的 WFP 是挂载在专用账户身份上的,身份不存在,这两关整体消失,而不是「变弱」。

做降级决策前,请把「这一层依赖哪个身份」问清楚。

提示三 · 通用规律:身份数覆盖策略组合数

按身份做访问控制时,身份数量必须覆盖策略组合数。网络策略有两档,就需要两个身份。

这和用两张门禁卡进两栋楼是同一件事,与 WFP 这个具体 API 无关。


五、第三关 · WFP 的运维真相

事实含义
用稳定 GUID 标识 + 持久标志重启后仍在
卸载 / 进程崩溃不会自动拆掉
仓库中检索不到删除用户或拆除 WFP 的函数清理靠人工或其他方式
WFP 失败 = 非致命安装继续往下走
防火墙失败 = 致命setup 整段失败
结论:这套方案的摩擦不在运行时,在安装和卸载。选它之前先确认环境允许建本地账户(企业策略可能直接禁止)。

六、思路三 · 为什么默认关着

6.1 AppContainer 为什么被否

要求AppContainer 的实际情况
workspace 可读、平台根可读,再按 deny 列表挖空可读范围默认很窄,任意路径读取必须改宿主 DACL
受限令牌 + ACL 明显更贴合这份权限模型。在业务代码里检索 AppContainer,零命中。

对照 DSH 把同一判断写成明文:AppContainer 令牌没有环境读权限,任意路径读取必须预先授权,对不上它的读模型。(另一方案 mxc 被否是因为系统版本底线太新。)

6.2 Elevated 为什么不做默认

要把读和网络都管住,就要:建账户 → 弹 UAC → 改防火墙 → 装 WFP。每一条都是安装摩擦,且企业策略可能禁止本地建用户。

6.3 枚举把默认值写成了关


pub enum WindowsSandboxLevel {
    #[default]
    Disabled,
    RestrictedToken,
    Elevated,
}

没写 windows.sandbox、两个 legacy flag 也没有时 → 落到 Disabled。legacy flag 已被标成 Removed。

产品判断:把默认设成关,等于把整套摩擦推迟到用户第一次真正启用时。好处是装上就能跑;代价是大多数用户从未走到那一档,也就从未拿到对应保护。

6.4 关掉之后:兜底在策略层


get_platform_sandbox(false) → Windows 上返回空
  → select_initial 变成 SandboxType::None
  → 命令仍可能被策略层拦住

测试锁死这条:没有后端时,只读档案 + Never 审批,未匹配的 cmd.exe /c dir 必须是 Decision::Forbidden。

跨平台能力不对等时,对外说法必须按实际启用的那一层写。 macOS 默认有 Seatbelt,Windows 默认可能是 Disabled —— 用户听到「有沙箱」,会默认三台机器是同一道墙。

七、横向对比 · 同一组标志,三种愿意付的代价

方案做法enforcement
DSH同组 CreateRestrictedToken 标志:`DISABLE_MAX_PRIVILEGE \LUA_TOKEN \WRITE_RESTRICTED`;不建账户、不装 WFPREADME 写明读/网络/进程可见性不受限,标为 partial
Claude Code原生 Windows 上写明没有沙箱;企业策略要求时 PowerShell 直接拒绝执行(文案 sandboxing is not available on native Windows)不假装有一层隔离
Grok Build沙箱 crate 平台写成 linux/landlock、macos/seatbelt,其余标 unknown;检索不到 AppContainer / RestrictedToken 实现不做 Windows 文件系统沙箱
Codex身份 + ACL + WFP + 专用账户全做进去摩擦更大,默认关

三档答案:

  1. 在 Windows 上不做文件系统沙箱
  2. 做写限制,装得上,读和网络留给别的机制
  3. 全做进去,代价是安装摩擦与卸载残留
技术栈高度重叠,差别全在产品判断:选择的依据不是能不能做,而是愿意让用户在安装时付多少代价。

八、审查清单

Windows 上部署前逐项确认:

  • 先确认本机 WindowsSandboxLevel 实际是 Disabled / RestrictedToken / Elevated
  • 若目标是挡住「读密钥 + 外传」,必须是 Elevated(令牌档做不到)
  • 企业策略是否允许创建本地用户(CodexSandboxOffline / CodexSandboxOnline)
  • 是否需要 UAC 提权,用户能否接受
  • WFP 过滤器已装 → 知道重启后仍在,且卸载不会自动拆
  • 清理方案已就绪(脚本或文档),不只是「卸载软件」
  • 代理场景走哪个身份(from_permissions 强制代理时选 Offline)
  • 沙箱关闭时的兜底:策略层是否把命令判成 Forbidden / Prompt
  • 对外文档承诺与实际启用档位一致(不写「已启用沙箱保护」如果默认是 Disabled)

九、约束说明

  1. 取材范围:仅使用本集素材,不跨集取材,不引入源码外的数据或性能测试结论。
  2. 源码快照:仓库 openai/codex,commit 4f39251a01,核对日期 2026-08-22;代码块保留源码原文。
  3. 教学化内容已标注:演示中的路径、账户名、开关组合为课程化设定;逻辑轨迹行号对应上述 commit。
  4. 横向对比各方均已核对源码(2026-08-22):DSH token.ts / win32-abi.ts / README / 设计笔记;Claude PowerShellTool.tsx;Grok types.rs。
  5. 不作推断:本页不推断防护强度百分比、攻击面覆盖率或企业环境的实际通过率;机制存在不等于效果达标。

十、一句话 Takeaway

Windows 没有 seatbelt 和 bubblewrap,隔离只能按身份锁资源。RestrictedToken 那三个标志管写,不管读和网络。要拦密钥和出网,必须再上专用账户、ACL 和按 SID 装的 WFP。AppContainer 对不上这份可读集合,Elevated 又要 UAC,所以默认关着,execpolicy 在没有后端时改成 Forbidden 或 Prompt。

来源:xueai.miyang.cn(小山学堂 · 洛小山《学 AI 产品,从入门到精通》)
本内容改编自小山学堂《学 AI 产品,从入门到精通》,为二次演绎配音版