第4节 安全边界与自主性控制

技术报告§4.1–4.8
Codex 通过"沙箱内自由、沙箱外审批"的双层安全架构,实现自主性与安全性的帕累托最优平衡。

4.1 核心公理:能力边界的形式化

Codex 安全体系建立在"能力边界"(capability boundary)这一核心概念之上。我们将智能体的动作空间 $\mathcal{A}$ 划分为三个互不相交的子集:

$$ \mathcal{A} = \mathcal{A}_{free} \sqcup \mathcal{A}_{gated} \sqcup \mathcal{A}_{denied} $$

其中:

沙箱的本质是操作系统强制的可达性子集:即使智能体"想要"执行动作 $a \in \mathcal{A}_{denied}$,操作系统级的访问控制也会在内核层面阻止其执行(见第28-29章)。这与应用级路径过滤有本质区别——应用级过滤可以通过漏洞或意外路径绕过,而 OS 级沙箱的强制性与应用正确性无关。

这种三划分通过两大配置"旋钮"实现:

$$ \text{安全配置} = (\text{SandboxMode}, \text{ApprovalPolicy}) $$

其中 `SandboxMode` $\in \{\text{ReadOnly}, \text{WorkspaceWrite}, \text{DangerFullAccess}\}$ 定义文件系统边界,`ApprovalPolicy` $\in \{\text{Never}, \text{OnRequest}, \text{UnlessTrusted}, \text{Granular}\}$ 定义审批触发条件(见第27章)。两个旋钮的组合形成"模式组合矩阵",不同的组合产生不同的 $\mathcal{A}_{free}$ 与 $\mathcal{A}_{gated}$ 划分。

4.2 自主-打扰权衡:双目标优化

长程智能体的安全控制本质上是在两个相互冲突的目标之间寻找平衡:

$$ \min_{\pi} \left[ \lambda_{ask}(\pi), \ R(\pi) \right] $$

其中:

打扰率与风险呈反比关系:严格的审批策略降低风险但增加打扰,宽松的策略减少打扰但增加风险。Codex 通过多层机制实现帕累托最优平衡:

  1. 边界触发而非计数触发:审批只在跨越沙箱边界时触发,而非每 $N$ 个操作触发。这意味着智能体在 $\mathcal{A}_{free}$ 内可以执行任意数量操作而零打扰。
  2. 会话级审批缓存:对于 $a \in \mathcal{A}_{gated}$,首次审批后可缓存为"会话级批准",后续相同操作自动放行。缓存键由环境ID、命令、工作目录、权限等构成(见第24章)。
  3. 断路器机制:当 Guardian 自动审查在单个 Turn 中连续拒绝超过阈值($N_{\text{consecutive}} = 3$)或滑动窗口内拒绝超过阈值($N_{\text{window}} = 10/50$)时,自动中断 Turn,防止智能体陷入反复尝试的死循环(见第32章)。

4.3 纵深防御的级联过滤

Codex 的安全控制采用"纵深防御"(defense-in-depth)原则,形成串联的过滤器链:

$$ \text{通过率} = \prod_{i=1}^{4} P_i, \quad \text{漏检率} = 1 - \prod_{i=1}^{4} (1 - \epsilon_i) $$

其中 $P_i$ 为第 $i$ 层的通过率,$\epsilon_i$ 为第 $i$ 层的漏检率。四层过滤器的串联结构确保即使某一层失效,后续层仍能提供保护。

4.3.1 第一层:静态策略分析

ExecPolicy 引擎基于 Google CEL 与 Starlark,在命令执行前进行静态规则匹配(见第30章)。对于命令 $a$,策略引擎输出三元决策:

$$ \text{ExecPolicy}(a) \in \{\text{Allow}, \text{Prompt}, \text{Forbidden}\} $$

其中 `Allow` 可携带 `bypass_sandbox=true` 标志,允许命令绕过沙箱直接执行;`Prompt` 触发用户审批流程;`Forbidden` 直接拒绝。

ExecPolicy 的关键设计是"前缀匹配":命令按前缀长度递增拆分(如 `cargo test -- --nocapture` 拆分为 `["cargo", "test"]`、`["cargo", "test", "--"]`、`["cargo", "test", "--", "--nocapture"]`),确保策略可以匹配不同粒度的命令模式。

4.3.2 第二层:审批门控

若 ExecPolicy 输出 `Prompt` 或无匹配规则,审批流程启动。Codex 实现了三层优先级的审查者链:

$$ \text{Hooks} \rightarrow \text{Guardian} \rightarrow \text{User} $$

Guardian 的评估基于结构化输出:

$$ \text{GuardianAssessment} = (\text{risk_level}, \text{user_authorization}, \text{outcome}, \text{rationale}) $$

其中 $\text{risk_level} \in \{\text{Low}, \text{Medium}, \text{High}, \text{Critical}\}$,$\text{outcome} \in \{\text{Approve}, \text{Reject}\}$。Guardian 严格的令牌限制(消息历史10k令牌,工具历史10k令牌)确保评估的高效性。

4.3.3 第三层:独立审查者

Guardian 作为独立审查者的设计体现了"审查者与执行者分离"的安全原则:

$$ \text{主智能体目标} \neq \text{Guardian 目标} $$

主智能体的目标是完成任务(可能倾向于冒险操作),Guardian 的目标是评估风险(倾向于保守决策)。这种目标冲突隔离确保审查不被任务目标污染。

Guardian 实现了失败关闭(fail-closed)原则:

$$ \Pr(\text{deny} \mid \text{uncertain}) = 1 $$

超时(90秒)、格式错误、执行失败时,默认拒绝。这与"宁多问一次,不放行一次"的错误成本不对称原则一致。

4.3.4 第四层:操作系统沙箱

即使所有上层检查通过,OS 级沙箱提供最后一道防线:

OS 级沙箱的不可绕过性来自其内核级别强制:即使应用存在漏洞,也无法通过沙箱边界访问受保护资源。

4.4 审批决策链与缓存的不对称性

Codex 的审批缓存采用"只缓存批准、不缓存拒绝"的不对称策略:

$$ \text{Cache}(a) = \begin{cases} \text{ApprovedForSession}, & \text{if } \text{decision}(a) = \text{Approved} \\ \text{None}, & \text{if } \text{decision}(a) \in \{\text{Denied}, \text{TimedOut}\} \end{cases} $$

这种设计源于错误成本的不对称性:

$$ C_{\text{false negative}} \gg C_{\text{false positive}} $$

其中 $C_{\text{false negative}}$ 是放行危险操作的代价(可能是数据丢失、系统破坏),$C_{\text{false positive}}$ 是多问一次的代价(仅是用户注意力)。缓存批准可以减少打扰,但不缓存拒绝确保每次危险操作都经过重新评估。

缓存键的设计体现了策略感知性:

$$ \text{CacheKey} = (\text{environment_id}, \text{command}, \text{cwd}, \text{permissions}, \text{policy\_fingerprint}) $$

其中 `policy_fingerprint` 是策略的哈希值,策略变更后缓存自动失效。

4.5 学习式策略演进:"批准一次,学习一次"

Codex 实现了策略的在线学习机制:每次用户批准操作后,系统建议生成对应的 ExecPolicy 规则:

$$ a \text{ 被批准} \implies \text{ExecPolicyAmendment}(a) $$

修正建议的结构为:

$$ \text{ExecPolicyAmendment} = (\text{program}, \text{prefix}, \text{decision}, \text{justification}) $$

用户确认后,规则被原子追加到策略文件(使用文件锁保证并发安全),策略重新加载。下次相同操作自动批准。

这种"批准一次,学习一次"的模式实现了策略的渐进式完善:随着时间推移,智能体的 $\mathcal{A}_{free}$ 逐渐扩大,打扰率逐渐降低,而安全性通过规则的可审计性得到保证。

断路器机制与策略学习形成正反馈:频繁拒绝触发断路器中断 Turn,用户可以事后分析拒绝模式,批量添加规则,避免未来的重复拒绝。

4.6 失败关闭原则的数学形式

Codex 安全体系全面采用失败关闭(fail-closed)原则。对于任何不确定状态,默认拒绝:

$$ \Pr(\text{deny} \mid \text{uncertain}) = 1 $$

不确定状态包括:

这一原则与安全工程中的"安全默认值"(secure by default)哲学一致:当系统无法做出确定的安全判断时,选择保守的拒绝而非冒险的放行。

失败关闭的数学表达为:

$$ \text{Safety}(\pi) = \min_{\text{states}} \text{Safety}_{\text{state}}(\pi) $$

系统的整体安全性由最不安全的状态决定,因此必须确保所有边界状态的安全性。

4.7 提权的最小权限通道

对于需要特殊权限的操作(如写入系统目录、访问受保护网络),Codex 实现了受控升级机制:

$$ \text{RequestPermissions} \rightarrow \text{UserApproval} \rightarrow \text{PermissionElevation} $$

提权请求的结构为:

$$ \text{PermissionRequest} = (\text{reason}, \text{permission\_profile}) $$

其中 `permission_profile` 明确指定请求的权限类型和范围。用户批准后,权限在会话内生效,会话结束自动撤销。

提权通道的设计遵循最小权限原则(principle of least privilege):

$$ \forall a \in \mathcal{A}: \text{Permission}(a) = \min \{ p \mid p \text{ enables } a \} $$

每个操作只获得其所需的最小权限集,避免过度授权。

提权的审计性与可撤销性通过以下机制保证:

4.8 设计原理剖析:为什么 OS 级沙箱优于应用级过滤

Codex 选择 OS 级沙箱而非应用级路径过滤,基于以下原理性分析:

4.8.1 不可绕过性

应用级过滤只能防止已知的危险操作路径,而沙箱可以限制所有系统调用。即使应用存在零日漏洞,沙箱仍能阻止其访问受保护资源。

形式化地,应用级过滤的安全性为:

$$ \text{Safety}_{\text{app}} = P(\text{no unknown path}) $$

而 OS 级沙箱的安全性为:

$$ \text{Safety}_{\text{os}} = 1 - P(\text{sandbox escape}) $$

由于 $P(\text{sandbox escape}) \ll P(\text{unknown path})$,OS 级沙箱提供更强的安全保证。

4.8.2 长时程风险累积

对于长程智能体,风险随时间累积:

$$ \Pr(\text{incident}) = 1 - \prod_{t}(1 - p_t) $$

其中 $p_t$ 是第 $t$ 步操作的风险概率。即使每步风险很低,长时间运行后,总体风险趋近于1。

因此,长程智能体需要护栏(guardrails)而非单次审批:护栏是持续生效的安全边界,而审批是单次的人工决策。Codex 的沙箱就是护栏的体现——它持续限制智能体的可达性,无需每步人工介入。

4.8.3 跨平台一致性与能力差异

不同操作系统的沙箱能力差异显著(见第29章对比表),但 Codex 通过统一抽象层实现跨平台一致性:

$$ \text{FileSystemSandboxPolicy} \rightarrow \text{PlatformAdapter} \rightarrow \text{PlatformSandbox} $$

统一抽象层定义了"最小公共能力集",平台适配器将平台特定能力映射到公共接口。对于不支持的特性,系统降级到基本功能或明确报告不支持。

这种设计确保智能体代码无需修改即可在不同平台上安全运行,同时充分利用各平台的高级安全特性。

本节符号表

符号含义
$\mathcal{A}$智能体动作空间
$\mathcal{A}_{free}$沙箱内免审批动作集合
$\mathcal{A}_{gated}$需审批动作集合
$\mathcal{A}_{denied}$策略禁止动作集合
$\pi$审批策略
$\lambda_{ask}(\pi)$打扰率(单位时间内人工干预次数)
$R(\pi)$风险暴露
$P_i$第 $i$ 层过滤器的通过率
$\epsilon_i$第 $i$ 层过滤器的漏检率
$C_{\text{false negative}}$放行危险操作的代价
$C_{\text{false positive}}$多问一次的代价
$p_t$第 $t$ 步操作的风险概率
$N_{\text{consecutive}}$连续拒绝阈值(默认3)
$N_{\text{window}}$滑动窗口拒绝阈值(默认10/50)
← 第3节第5节 →