OpenAI Codex 安全机制架构详解
近期 Openclaw 项目引发了 Agent 安全讨论——用户担心 AI 执行恶意命令、修改敏感文件、泄露密钥。Codex 作为先行者,其多层防护机制为同类项目提供了可借鉴的安全范式。理解 Codex 如何解决问题,有助于评估和改进其他 Agent 的安全设计。
广泛采用:作为 OpenAI 官方推出的 Coding Agent,Codex 是目前使用最广泛的 AI 编程工具之一。其安全架构经过大量用户验证,代表了业界较高水平。
开源透明:Codex 核心代码(codex-rs)在 GitHub 开源,可以深入分析其安全实现细节,而非依赖黑盒猜测。这为理解"生产级 AI Agent 如何做安全"提供了难得的学习样本。
1. 问题背景
AI 执行命令存在三类风险:
- 命令错误:AI 误判导致执行错误命令
- 命令注入:AI 被诱导执行恶意命令
- 程序替换:别名、PATH 劫持导致执行非预期程序
此外,AI 运行过程中还涉及敏感信息保护问题:API Key 可能泄露到日志、进程内存可能被窃取、用户密码可能被输出到终端。Codex 通过多层防护机制解决这些问题。
2. 整体架构
┌─────────────────────────────────────────────────────────────────┐
│ 命令执行请求 │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ 第一层:执行策略(决策层) │ │
│ │ • 命令安全检查:安全白名单 / 危险黑名单 │ │
│ │ • 规则匹配:Allow / Prompt / Forbidden │ │
│ │ • Shell Tool MCP:拦截 execve(),获取真实程序路径 │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ 第二层:进程加固(防护层) │ │
│ │ • 禁止调试器附加(ptrace) │ │
│ │ • 禁止动态库注入(LD_PRELOAD / DYLD_INSERT_LIBRARIES) │ │
│ │ • 禁止 core dump │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ 第三层:沙箱隔离(限制层) │ │
│ │ • 文件系统:只读默认 + 白名单可写 │ │
│ │ • 网络隔离:完全隔离 / 代理模式 │ │
│ │ • 系统调用过滤:seccomp │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ 独立模块:Secrets 管理 │ │
│ │ • 敏感信息存储:OS Keyring + age 加密 │ │
│ │ • 输出脱敏:自动隐藏 API Key / Token / 密码 │ │
│ └─────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
3. 第一层:执行策略
当用户在 Shell 中输入 ls -la 时,Shell 最终会调用 execve() 系统函数:
int execve(const char *pathname, char *const argv[], char *const envp[]);
这个函数接收三个参数:要执行的程序路径(如 /bin/ls)、参数列表和环境变量。Codex 的第一层防护正是通过拦截这个调用,在程序真正执行前进行安全检查。
3.1 决策类型
| 决策 | 行为 | 使用场景 |
|---|---|---|
| Allow | 直接执行 | 已知安全的命令 |
| Prompt | 弹窗确认 | 需要用户批准的操作 |
| Forbidden | 拒绝执行 | 明确禁止的命令 |
优先级:Forbidden > Prompt > Allow
3.2 命令安全检查
在规则匹配之前,Codex 首先进行命令安全检查,分为两个维度:
安全白名单(is_known_safe_command):这些命令只读、无副作用,自动判定为安全:
cat, cd, cut, echo, expr, false, grep, head, id, ls,
nl, paste, pwd, rev, seq, stat, tail, tr, true, uname,
uniq, wc, which, whoami
部分命令需要检查参数:
| 命令 | 安全条件 | 危险参数示例 |
|---|---|---|
base64 |
无输出文件参数 | -o, --output |
find |
无执行/删除操作 | -exec, -delete, -fls |
rg |
无外部命令执行 | --pre, --search-zip |
git |
只读子命令 | push, reset --hard |
sed |
只读模式 | 非 -n {N}p 格式 |
对于 bash -lc "..." 形式的复合命令,Codex 会解析其中的每个子命令,只有全部安全才判定为安全。
危险黑名单(command_might_be_dangerous):这些命令强制触发用户确认:
// rm -f / rm -rf 强制需要确认
Some("rm") => matches!(command.get(1), Some("-f" | "-rf")),
// sudo 检查子命令是否危险
Some("sudo") => is_dangerous_to_call_with_exec(&command[1..]),
// 危险的 git 子命令
Some("git") => matches!(subcommand, "push" | "reset" | "checkout" | "rebase"),
// 网络相关
Some("curl" | "wget") => true, // 可能下载数据
Some("nc" | "ncat") => true, // 网络通信
3.3 规则匹配流程
命令: npm install lodash
│
▼
┌────────────────────────────────────────┐
│ 1. 命令安全检查 │
│ • 安全白名单? → Allow │
│ • 危险黑名单? → Prompt │
└────────────────────────────────────────┘
│ 不在名单
▼
┌────────────────────────────────────────┐
│ 2. 规则匹配 │
│ • 用程序名作为索引查找规则 │
│ • 检查子命令/参数是否匹配 │
│ • 匹配成功 → 返回对应决策 │
└────────────────────────────────────────┘
│ 未匹配
▼
┌────────────────────────────────────────┐
│ 3. 路径解析 │
│ • 提取程序名(处理完整路径) │
│ • 验证路径是否在允许列表 │
└────────────────────────────────────────┘
│ 未匹配
▼
┌────────────────────────────────────────┐
│ 4. 默认回退 │
│ 根据用户配置决定: │
│ • suggest: 建议确认(默认) │
│ • auto-edit: 自动批准编辑 │
│ • full-auto: 全自动执行 │
└────────────────────────────────────────┘
3.4 自定义规则
用户在 .codex/rules 文件中定义:
# 允许 npm install/run/test
rule(
pattern = ["npm", ["install", "run", "test"]],
decision = "allow",
)
# 禁止 sudo
rule(
pattern = ["sudo"],
decision = "forbidden",
)
# git push 需确认
rule(
pattern = ["git", "push"],
decision = "prompt",
)
3.5 Approval 确认流程
当决策为 Prompt 时,Codex 通过 TUI 界面向用户展示确认请求:
┌─────────────────────────────────────────────────────────────────┐
│ ⚠️ Approval Required │
│ │
│ Command: rm -rf ./dist │
│ Reason: Destructive operation detected │
│ │
│ [Y] Approve [N] Deny [E] Edit [A] Approve All │
└─────────────────────────────────────────────────────────────────┘
用户可以配置确认策略(approval_policy):
| 策略 | 行为 |
|---|---|
suggest |
危险操作建议确认(默认) |
on-failure |
仅失败时确认 |
on-request |
仅用户请求时确认 |
never |
从不确认(危险) |
3.6 Shell Tool MCP:execve 拦截机制
规则匹配依赖命令名,但实际执行的程序可能不同:
alias ls='rm -rf /' # 别名替换
export PATH="~/bin:$PATH" # PATH 中放置恶意程序
ln -s /bin/rm ~/bin/ls # 符号链接
要解决这个问题,需要在 execve() 被调用时获取真实的程序路径。Codex 通过修改 Shell 实现这一点。
架构设计
Codex 使用 MCP(Model Context Protocol)协议实现 Shell 工具。MCP 是 Anthropic 提出的 AI 工具调用标准,定义了模型与外部工具之间的通信方式。主进程与 Shell 之间通过 Unix Domain Socket 通信——这是一种本地进程间通信机制,比 TCP socket 更高效,不经过网络栈,直接在内核中传递数据,且支持文件描述符传递。
┌─────────────────────────────────────────────────────────────────┐
│ Codex 主进程 │
│ │ │
│ │ 启动 Shell 时设置环境变量: │
│ │ EXEC_WRAPPER=/path/to/codex-execve-wrapper │
│ │ CODEX_ESCALATE_SOCKET=<fd> │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ 打补丁的 Shell(bash-fork / zsh-fork) │ │
│ │ │ │
│ │ 正常 Shell: │ │
│ │ execve("/bin/ls", ["ls", "-la"], env) │ │
│ │ │ │
│ │ 打补丁后: │ │
│ │ if (EXEC_WRAPPER 环境变量存在) { │ │
│ │ execve(EXEC_WRAPPER, ["/bin/ls", "ls", "-la"]) │ │
│ │ } │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ codex-execve-wrapper │ │
│ │ │ │
│ │ 1. 解析参数:file="/bin/ls", argv=["ls", "-la"] │ │
│ │ 2. 通过 Unix Socket 发送请求到主进程: │ │
│ │ EscalateRequest { │ │
│ │ file: "/bin/ls", │ │
│ │ argv: ["ls", "-la"], │ │
│ │ workdir: "/home/user/project", │ │
│ │ env: {...} │ │
│ │ } │ │
│ │ 3. 等待主进程响应: │ │
│ │ EscalateResponse { action: Run | Escalate | Deny } │ │
│ │ 4. 根据响应执行或拒绝 │ │
│ └─────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
通信协议
请求结构:
struct EscalateRequest {
file: PathBuf, // 解析后的绝对路径
argv: Vec<String>, // 参数列表(含 argv[0])
workdir: PathBuf, // 工作目录
env: HashMap<String, String>,
}
响应结构:
enum EscalateAction {
Run, // 允许执行
Escalate, // 提升权限执行(脱离沙箱)
Deny { reason: Option<String> }, // 拒绝执行
}
wrapper 收到的是解析后的绝对路径 /bin/ls,而非命令名 ls,因此可以准确判断实际执行的程序。
4. 第二层:进程加固
即使命令被允许执行,进程本身仍可能受到攻击。攻击者可能通过调试器读取进程内存中的敏感数据(如 API Key),或通过动态库注入劫持进程行为。
4.1 攻击向量
ptrace 攻击:ptrace 是 Unix 系统调用,用于进程调试。调试器(如 gdb)通过它附加到目标进程,可以读取/写入进程内存、单步执行指令、捕获信号。攻击者同样可以利用 ptrace 读取进程中的敏感数据。
动态库注入:
- Linux 上,攻击者可设置
LD_PRELOAD环境变量,指定在程序启动前加载的动态库,从而覆盖程序调用的函数:
LD_PRELOAD=/tmp/malicious.so /usr/bin/some-program
- macOS 上,等效的环境变量是
DYLD_INSERT_LIBRARIES。
core dump 泄露:进程崩溃时可能生成内存转储文件(core dump),其中可能包含敏感信息。
4.2 加固实现
Codex 在 main() 之前执行 pre_main_hardening() 函数(通过 #[ctor::ctor] 宏实现),确保进程启动时就处于安全状态。
Linux:
// 1. 禁止 ptrace 附加
prctl(PR_SET_DUMPABLE, 0, 0, 0, 0);
// 2. 禁止 core dump
setrlimit(RLIMIT_CORE, 0);
// 3. 清除动态库注入变量
unsetenv("LD_PRELOAD");
unsetenv("LD_LIBRARY_PATH");
// ... 清除所有 LD_* 变量
macOS:
// 1. 禁止调试器附加
ptrace(PT_DENY_ATTACH, 0, 0, 0);
// 2. 禁止 core dump
setrlimit(RLIMIT_CORE, 0);
// 3. 清除动态库注入变量
unsetenv("DYLD_INSERT_LIBRARIES");
unsetenv("DYLD_LIBRARY_PATH");
// ... 清除所有 DYLD_* 变量
Windows:
// 禁止调试器附加
SetProcessDebugPort(GetCurrentProcess(), (HANDLE)-1);
任何一步失败,进程直接退出,确保进程要么安全启动,要么不启动:
const PRCTL_FAILED_EXIT_CODE: i32 = 5;
const PTRACE_DENY_ATTACH_FAILED_EXIT_CODE: i32 = 6;
const SET_RLIMIT_CORE_FAILED_EXIT_CODE: i32 = 7;
5. 第三层:沙箱隔离
前两层防护控制"能执行什么命令"和"进程是否安全",第三层则限制"命令能访问什么资源"。即使恶意命令被执行,沙箱也能将其损害控制在有限范围内。
5.1 设计原则
- 默认拒绝:所有访问默认禁止
- 白名单开放:显式声明需要的权限
- 敏感路径保护:
.git,.codex强制只读
5.2 Linux 实现
Linux 沙箱由多个内核特性组合实现:
Namespace:资源隔离
Namespace 是 Linux 内核提供的隔离机制,让进程看到的是"虚拟化"的系统视图:
| Namespace 类型 | 隔离内容 |
|---|---|
| Mount | 文件系统挂载点 |
| PID | 进程 ID |
| Network | 网络栈(网卡、路由、iptables) |
| User | 用户和组 ID |
| UTS | 主机名和域名 |
容器技术(Docker、Podman)本质上就是组合使用各种 namespace。
seccomp:系统调用过滤
seccomp(Secure Computing Mode)用于限制进程可调用的系统调用。进程被 seccomp 限制后,调用被禁止的系统调用会被内核直接终止:
// 示例:只允许 read, write, exit
struct sock_filter filter[] = {
BPF_STMT(BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, nr)),
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_read, 0, 2),
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_write, 0, 1),
BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW),
BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_KILL),
};
Codex 禁止的系统调用:
| 调用 | 原因 |
|---|---|
ptrace |
攻击其他进程 |
process_vm_readv |
读取其他进程内存 |
process_vm_writev |
写入其他进程内存 |
io_uring_* |
可绕过安全检查 |
kexec_load |
加载内核 |
init_module |
加载内核模块 |
网络相关的精细控制:
// socket() 调用根据 domain 过滤
if (domain == AF_INET || domain == AF_INET6) {
return EPERM; // 禁止网络 socket
}
if (domain == AF_UNIX) {
return allow(); // 允许 Unix socket
}
Bubblewrap:文件系统隔离
Bubblewrap(bwrap)是 Flatpak 项目开发的轻量级沙箱工具,通过组合 namespace 实现文件系统隔离。相比 Docker,它更轻量,无需 daemon,适合桌面应用沙箱化。
┌─────────────────────────────────────────────────────────────┐
│ 主机系统 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ bwrap 创建的隔离环境 │ │
│ │ • 独立的 mount namespace(文件系统视图) │ │
│ │ • 独立的 user namespace(用户 ID 映射) │ │
│ │ • 独立的 pid namespace(进程 ID 隔离) │ │
│ │ • 可选的 network namespace(网络隔离) │ │
│ │ │ │
│ │ 内部进程看到的文件系统: │ │
│ │ /bin/ → 主机 /bin/(只读) │ │
│ │ /home/ → 主机 /home/(只读) │ │
│ │ /project/ → 主机 /home/user/project/(可写) │ │
│ │ /etc/passwd → 不可见 │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
Bubblewrap 参数构建:
bwrap \
--new-session # 创建新会话
--die-with-parent # 父进程退出时终止
--unshare-user # 用户命名空间隔离
--unshare-pid # PID 命名空间隔离
--unshare-net # 网络命名空间隔离(可选)
--proc /proc # 挂载 /proc
--ro-bind / / # 根文件系统只读
--dev /dev # 最小 /dev(含 urandom)
--bind /project /project # 项目目录可写
--ro-bind /project/.git /project/.git # .git 只读
--chdir /project # 设置工作目录
-- command...
挂载顺序(重要):后挂载的会覆盖先挂载的,实现细粒度控制:
1. --ro-bind / / # 根只读
2. --dev /dev # 最小 /dev
3. --bind /project /project # 可写目录覆盖
4. --ro-bind /project/.git # 子目录再次只读(覆盖可写)
5. --tmpfs /blocked # 遮蔽禁止访问的路径
Landlock:备选方案
Landlock 是 Linux 5.13+ 内核特性,提供细粒度的文件系统访问控制。当 Bubblewrap 不可用时,Codex 会回退到使用 Landlock。
5.3 macOS 实现:Seatbelt
macOS 使用内置的 Seatbelt 沙箱机制(又称 App Sandbox),通过 /usr/bin/sandbox-exec 执行命令。Seatbelt 使用 SBPL(Seatbelt Policy Language)编写策略,内核强制执行,进程无法绕过。
SBPL 示例:
(version 1)
; 默认拒绝所有操作
(deny default)
; 允许执行程序
(allow process-exec)
; 文件系统权限
(allow file-read* (subpath "/System"))
(allow file-read* (subpath "/usr"))
(allow file-read* (subpath "/Users/user/project"))
(allow file-write* (subpath "/Users/user/project"))
; 保护敏感目录
(deny file-write* (subpath "/Users/user/project/.git"))
; 网络权限
(deny network*)
(allow network-outbound (to "registry.npmjs.org"))
(allow network-outbound (to unix-socket))
常用规则:
| 规则 | 说明 |
|---|---|
(deny default) |
默认拒绝所有 |
(allow process-exec) |
允许执行程序 |
(allow file-read* (subpath "...")) |
允许读取指定目录 |
(allow file-write* (subpath "...")) |
允许写入指定目录 |
(deny file-write* (subpath "...")) |
禁止写入指定目录(覆盖 allow) |
(allow network-outbound (to "...")) |
允许出站网络连接 |
5.4 网络隔离模式
| 模式 | 行为 | 使用场景 |
|---|---|---|
| FullAccess | 不隔离网络 | 需要完整网络访问 |
| Isolated | 完全禁止网络 | 离线任务 |
| ProxyOnly | 通过本地代理 | 只允许白名单域名 |
ProxyOnly 模式下,bwrap 创建网络命名空间,启动代理进程路由流量。
5.5 平台对比
| 特性 | Linux | macOS |
|---|---|---|
| 文件隔离 | Bubblewrap | Seatbelt |
| 网络隔离 | seccomp + namespace | Seatbelt |
| 系统调用过滤 | seccomp | 无(依赖 Seatbelt) |
| 用户隔离 | user namespace | 无(依赖 Seatbelt) |
6. Secrets 管理
前三层防护聚焦于命令执行的边界控制,但 AI 运行过程中还有一类风险:敏感信息的存储和泄露。Codex 通过独立的 Secrets 模块解决这个问题,该模块与三层架构并行运作。
6.1 敏感信息存储
用户通过 codex secrets set 命令存储敏感信息(如 API Key、数据库密码):
# 存储全局 secret
codex secrets set OPENAI_API_KEY
# 存储项目级 secret
codex secrets set DATABASE_URL --env my-project
存储流程:
用户输入: sk-xxxxxxxxxxxxxxxx
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ Secrets Backend 选择 │
│ │
│ 优先级: │
│ 1. OS Keyring(推荐) │
│ • macOS: Keychain │
│ • Windows: Credential Manager │
│ • Linux: Secret Service (GNOME Keyring / KWallet) │
│ 2. 本地加密文件(备选) │
│ • 位置: ~/.codex/secrets/local.age │
│ • 加密: age 加密库 │
│ • 密钥: 存储在 OS Keyring │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ 存储结构 │
│ │
│ Keyring: │
│ Service: "codex" │
│ Account: "global/OPENAI_API_KEY" 或 "env/<id>/DATABASE_URL" │
│ Password: <加密后的主密钥> │
│ │
│ local.age: │
│ { │
│ "version": 1, │
│ "secrets": { │
│ "global/OPENAI_API_KEY": "sk-xxxxxxxx", │
│ "env/my-project/DATABASE_URL": "postgres://..." │
│ } │
│ } │
└─────────────────────────────────────────────────────────────────┘
为什么使用 OS Keyring?
- 安全存储:操作系统级别的加密存储,密钥不会明文出现在磁盘上
- 自动锁定:用户锁屏时自动锁定 keyring
- 跨应用共享:同一用户的应用可以安全共享 secrets
- 备份集成:macOS Keychain 支持 iCloud 同步
6.2 输出脱敏
敏感信息可能通过多种途径泄露:AI 输出包含 API Key、错误消息暴露密码、日志记录 token。Codex 在输出前自动检测并脱敏敏感信息。
检测规则:
// OpenAI API Key
OPENAI_KEY_REGEX: sk-[A-Za-z0-9]{20,}
// AWS Access Key ID
AWS_ACCESS_KEY_ID_REGEX: AKIA[0-9A-Z]{16}
// Bearer Token
BEARER_TOKEN_REGEX: Bearer [A-Za-z0-9._\-]{16,}
// 通用敏感赋值
SECRET_ASSIGNMENT_REGEX:
(api_key|token|secret|password) [:=] "...value..."
脱敏流程:
AI 输出: "Your API key sk-proj-xxxxxxxxxxxxxxxx is ready"
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ redact_secrets() 函数处理 │
│ │
│ 1. 匹配 OPENAI_KEY_REGEX → 替换为 [REDACTED_SECRET] │
│ 2. 匹配 AWS_ACCESS_KEY_ID_REGEX → 替换为 [REDACTED_SECRET] │
│ 3. 匹配 BEARER_TOKEN_REGEX → 替换为 Bearer [REDACTED_SECRET] │
│ 4. 匹配 SECRET_ASSIGNMENT_REGEX → 替换为 xxx=[REDACTED_SECRET]│
└─────────────────────────────────────────────────────────────────┘
│
▼
终端输出: "Your API key [REDACTED_SECRET] is ready"
6.3 与沙箱的协作
Secrets 管理与沙箱机制协同工作:
- 进程加固确保 secrets 存储在 keyring 中的密钥不被 ptrace 读取
- 沙箱隔离限制 secrets 文件只能被 codex 进程访问
- 输出脱敏在 TUI 显示前自动执行,防止泄露到终端或日志
7. 完整执行流程
AI 请求: npm install lodash
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ 第一层:执行策略 │
│ │
│ 1. 命令安全检查: npm 不在白名单,也不在黑名单 │
│ 2. 规则匹配: npm install → Allow │
│ 3. Shell MCP 准备: │
│ - 设置 EXEC_WRAPPER=/opt/codex/codex-execve-wrapper │
│ - 创建 Unix Socket,fd=3 │
│ - 设置 CODEX_ESCALATE_SOCKET=3 │
│ 4. 启动 Shell: bwrap ... bash-fork -lc "npm install lodash" │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ Shell 内部 │
│ │
│ bash 解析命令 → 找到 npm │
│ 执行 execve("/usr/bin/npm", ["npm", "install", "lodash"], env) │
│ │
│ 打补丁的 bash 检测到 EXEC_WRAPPER: │
│ → execve(EXEC_WRAPPER, ["/usr/bin/npm", "npm", "install", ...])│
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ codex-execve-wrapper │
│ │
│ 1. 从 argv 获取: file="/usr/bin/npm", argv=["npm", ...] │
│ 2. 通过 fd=3 发送 EscalateRequest 到主进程 │
│ 3. 主进程检查规则,返回 EscalateResponse { action: Run } │
│ 4. wrapper 执行 execve("/usr/bin/npm", ...) │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ 沙箱内执行 │
│ │
│ /usr/bin/npm install lodash │
│ - 可读: / (整个文件系统) │
│ - 可写: /home/user/project │
│ - 只读: /home/user/project/.git │
│ - 网络: 可访问 registry.npmjs.org │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ 输出处理 │
│ │
│ npm 输出 → redact_secrets() → 终端显示 │
│ (如有 API Key 会自动替换为 [REDACTED_SECRET]) │
└─────────────────────────────────────────────────────────────────┘
8. 安全边界总结
| 层级 | 防护目标 | 实现机制 | 失效时影响 |
|---|---|---|---|
| 第一层 | 控制哪些命令能执行 | 命令检查 + 规则匹配 + execve 拦截 | 第二层仍能保护进程 |
| 第二层 | 保护进程本身不被攻击 | ptrace 禁止 + 环境清理 | 第三层仍能限制资源访问 |
| 第三层 | 限制命令能访问的资源 | namespace + seccomp + Seatbelt | 最后防线 |
| Secrets | 保护敏感信息存储和输出 | OS Keyring + age 加密 + 输出脱敏 | 独立于三层架构 |
参考资料
- Codex 源码:
codex-rs/execpolicy/- 规则匹配codex-rs/shell-command/src/command_safety/- 命令安全检查codex-rs/shell-escalation/- execve 拦截codex-rs/process-hardening/- 进程加固codex-rs/linux-sandbox/- Linux 沙箱codex-rs/core/seatbelt.rs- macOS 沙箱codex-rs/secrets/- Secrets 管理
- Linux seccomp: https://man7.org/linux/man-pages/man2/seccomp.2.html
- Bubblewrap: https://github.com/containers/bubblewrap
- macOS Seatbelt: https://wiki.mozilla.org/Sandbox/OS_X_Sandbox_Template
- age encryption: https://github.com/C2SP/CCTV-Age