在日常工作中,AI Coding Agent 基本上已经成了离不开的工具。它们生成的代码质量往往比开发者自己写的还要好,这几乎成了行业共识。但在使用过程中会发现,几乎每个工具都有一组权限设置,每个档位对应 Agent 能触达的操作边界。
这套权限功能本质上就是一套运行在客户端的沙箱系统。身为开发者,自然很想搞清楚这背后的实现原理——顺便也琢磨一下,能不能自己动手搓一个专属的 Coding Agent 来干点脏活累活。
为什么需要沙箱
直接给 Agent 完整的真实环境?风险太大。Agent 一旦因为幻觉、误判或者被恶意注入危险指令,就可能执行不可逆的操作——比如把服务器上的关键数据删掉,读取本地的私钥信息,甚至偷偷把 .env 里的密钥发到未知服务器。
想象一下这些场景:让 AI 帮忙写个 Python 脚本导出数据,它顺手来了个 rm -rf,删掉了根本没让改的文件;让它跑项目里的测试代码,它直接把电脑上的各种私钥读走了;你以为写好 prompt 就能安心等结果,结果它暗中把密钥外传了。
当一个 Coding Agent 能够“自己输出命令并且自己跑”的时候,它就像一个黑盒——输出的命令根本没法预先控制。本质上,这就是把一段不可信代码放进你的电脑里直接执行。这种情况下,你敢直接按回车吗?别犹豫,这时候就需要沙箱工具。
沙箱做的事情很朴素:给 Agent 套上一件枷锁,让它带着镣铐去实现需求。它无法触碰没有被授权的事情,也就不会碰你的密钥、不会胡乱删除系统文件、不会在执行恶意脚本时向未知域名发送信息。
六条设计原则
不管是 Codex、Claude Code,还是自己动手实现一个 Coding Agent,沙箱设计都绕不开下面几条原则。理解了这些,后面再看具体实现会轻松很多。
默认拒绝,而不是默认放行
能不开放权限就不开放,用白名单思维而非黑名单。Codex 的 workspace-write 模式,默认只对当前工作目录开放写权限,其他目录默认只读;Claude Code 默认不允许请求任何公网域名,新域名第一次出现时会向用户请求授权。
最小权限
只给工作目录写权限,网络默认关闭,密钥文件(~/.ssh、~/.aws、.env)对 Agent 不可见。Claude Code 甚至能显式地把某些文件和环境变量设为 deny。
隔离要分四个维度
文件系统、网络、进程、系统调用,一层层收束。只锁文件不够——Agent 能读能写,联网照样可以把数据发走;只锁网络也不够——它能改本地的 git config,直接把代码推送到未知仓库。四个维度必须一起限制才有效。
把“策略”和“怎么落地”分开
这一点设计得非常巧妙。写一套“我想要什么隔离级别”的策略描述,背后按客户端所在操作系统映射到不同的原生机制:macOS 用 Seatbelt,Linux 用 bubblewrap 加 seccomp,Windows 用受限令牌加 ACL。上层只管策略,下层管落地。
自治和安全的取舍
沙箱越严格,Agent 越是能连续跑不中断。反过来,如果每一步操作都需要人工审批,按了二十次 Approve 之后基本上就是在无脑点了——这才是真正的安全幻觉。沙箱本质上是用系统级边界来换取使用者有限的注意力。
纵深防御,别神话它
沙箱不是银弹,它只是第一道墙,需要配合权限模型、代码审查、依赖审计一起用。
落地:CodeX 和 Claude Code 沙箱方案探索
Codex:三套原生隔离,藏在统一策略后面
Codex 的沙箱是典型的“策略抽象 + 多平台实现”:
- macOS 12+:用系统自带的 Seatbelt 加
sandbox-exec工具,能根据 Agent 请求的权限等级动态生成 SBPL(Sandbox Profile Language)权限控制脚本。 - Linux:bubblewrap(bwrap)加 seccomp 的组合。bwrap 用 mount namespace 把根目录设为只读,再把工作目录设为可写;seccomp 在进程内拦掉
connect/bind/ptrace等系统调用。 - Windows:用受限令牌(restricted token)加 ACL 来修改文件权限,再通过专门的 IPC 提权运行器来执行。
权限分级跨平台通用:ReadOnly(只读挂载)、WorkspaceWrite(工作区可写、敏感目录强制只读),Linux 上网络还有三档 Isolated / ProxyOnly / FullAccess。
Claude Code:sandboxed Bash tool
Claude Code 也提供了 OS 级沙箱,而不是仅靠权限审批。它提供的是一个 sandboxed Bash tool,给 Bash 命令做文件系统和网络隔离,底层 macOS 用 Seatbelt、Linux/WSL2 用 bubblewrap(再加 socat 做网络袋里)。原生 Windows 不支持,得跑在 WSL2 里。(根据官方文档描述)
开启方式很简单:
- 会话里敲
/sandbox,选 auto-allow 或 regular permissions; - 或者在
.claude/settings.local.json/~/.claude/settings.json进行配置:{ "sandbox": { "enabled": true } }
隔离策略:
- 文件系统:当前工作目录和会话临时目录默认可写,但全机可读。需要留意的是,默认情况下它还能读到
~/.ssh这类内容,除非开发者显式 deny。 - 网络:默认不预放行任何域名,新域名首次出现会向用户请求放行权限;可以配置
allowedDomains预放行。 - 密钥:能显式 deny 文件或环境变量,比如把
GITHUB_TOKEN设成 deny。
权限审批和沙箱是两层互补的关系:权限规则管“工具能不能跑”(所有 tool 通用,运行前生效),沙箱管“Bash 跑的时候 OS 层怎么隔离”(只管 Bash,运行时生效)。/sandbox 不是另一种权限模式,二者是叠在一起的。
对比
| Agent | Codex | Claude Code |
|---|---|---|
| 隔离层级 | OS 原生(Seatbelt / bwrap+seccomp) | OS 级 Bash 沙箱(Seatbelt / bubblewrap) |
| 策略抽象 | 统一沙箱策略 + 多平台实现 | 权限模型 + Bash 沙箱 |
| 默认行为 | 按 --sandbox 等级(ReadOnly / WorkspaceWrite) | 配置开启;命令走权限审批 |
| 网络策略 | 默认禁止(按等级划分,Linux 有 Isolated/ProxyOnly/FullAccess 不同等级) | 默认不预放行,新域名向用户请求;可配置预放行列表 |
| 平台覆盖 | macOS / Linux / Windows | macOS / Linux / WSL2(原生 Win 不支持) |
| 密钥保护 | 敏感目录强制只读(.git/.agents/.codex) | 需显式配置禁用的文件或变量;默认电脑上所有文件可读 |
实测
Codex(macOS)选择 workspace-write 模式后,实际生成的 SBPL 大概如下:
(version 1) ; 声明版本号
(deny default); 默认拒绝
(allow process-exec*) ; 允许运行执行程序
(allow file-read* (subpath "/")) ; 全机可读
(allow file-write* (subpath "/path/workspace")) ; 工作区可写
(deny file-write* (subpath "/path/workspace/.git")) ; 敏感目录强制只读
(deny file-write* (subpath "/path/workspace/.agents"))
(deny file-write* (subpath "/path/workspace/.codex"))
(deny network*); 默认禁止通过网络外联逐行看下来:deny default 是默认拒绝;file-write* 只放开工作区;.git 那三行强制只读,防止它改 git config;最后 deny network* 把外联掐了。
也可以自己在电脑上测试 sandbox-exec 禁止所有网络请求的情况。sandbox-exec 是 macOS 自带的命令,终端里直接使用:
sandbox-exec -p '(version 1)(allow default)(deny network*)' curl https://www.dimples.top/第一条命令默认允许所有但禁用网络((deny network*)),使用 curl 请求自己的博客地址,发现直接无法解析域名。
去掉网络限制之后,请求恢复正常。命令如下:
sandbox-exec -p '(version 1)(allow default)' curl https://www.dimples.top/Codex(Linux)的 bwrap 命令行大致是:
bwrap --unshare-user --unshare-pid --unshare-net --ro-bind / / --bind /path/to/workspace /path/to/workspace --bind /path/to/workspace/.git /path/to/workspace/.git --read-only--unshare-net 直接把网络 namespace 摘掉,--ro-bind / / 把根挂载为只读,再 bind 回工作区可写。配合 seccomp 拦 connect / bind,四个维度就齐了。
Claude Code 开启 /sandbox 后,跑同一条 curl api.github.com,会看到它被袋里拦下、弹 prompt 问是否放行这个域名——这就是网络隔离在运行时生效的证据。
没有绝对的安全
必须认识到:沙箱不等于绝对的安全,不能过度神话。下面这些情况也能绕过沙箱直接提权进行非授权操作:
- 供应链投毒:恶意 npm 包在沙箱内照样能外联、读取项目内自己的
.env(它就在工作区里,沙箱管不着); - 0day / 逃逸:OS 级沙箱也可能被逃逸(bwrap/seccomp 历史上都有过);
- 配置错误:Codex 容器里主机不支持 Landlock/seccomp 时,沙箱可能静默失效;Claude Code 依赖没装(bubblewrap/socat)时默认只是 warning 然后直接运行,除非设了
failIfUna vailable: true。
沙箱的价值不是消灭风险,而是把大概率翻车的事件压到一个可接受的范围。真要拿 Agent 写代码,还是得配合代码审查、依赖审计以及最小权限账号一起上。
总结
以上是关于 Coding Agent 沙箱的学习笔记。下一步打算拿学到的这些思路,自己搓一个专属的 Coding Agent 来干点脏活累活,到时候再写实战篇。如果这篇内容对你有帮助,欢迎在评论区聊聊你踩过 Agent 的坑——真实案例总是最有价值的参考。
