Claude Code 沙箱安全机制:双层隔离
“少弹权限框”不是安全目标,限制 blast radius 才是。Claude Code 沙箱用文件系统与网络两道边界约束 Bash 及其子进程;权限规则则决定哪些工具可以被尝试。两层要一起使用。
沙箱到底拦什么
Claude 决定调用 Bash
↓ 权限规则:这类命令允许尝试吗?
Sandboxed Bash
├─ 文件系统:可读/可写路径
└─ 网络代理:允许/拒绝域名
↓
命令及所有子进程继承相同边界
| 控制面 | 作用范围 | 典型配置 |
|---|---|---|
| Permissions | Bash、Read、Edit、WebFetch、MCP 等工具 | allow / ask / deny |
| Filesystem sandbox | Bash 和子进程 | allowWrite、denyRead、denyWrite |
| Network sandbox | Bash 和子进程的出站连接 | allowedDomains、deniedDomains |
| Container/VM | 整个进程与操作系统视图 | 用户、挂载、namespace、seccomp |
沙箱只覆盖 Bash 命令及子进程,不能自动约束所有 MCP server,也不能替代容器、最小权限凭证或代码审查。
平台实现
- macOS 使用 Seatbelt。
- Linux 与 WSL2 使用 bubblewrap;网络代理还需要
socat。 - WSL1 不支持所需的内核隔离能力。
- Linux/WSL2 中的沙箱会阻止通过 Unix socket 启动 Windows 二进制。
Ubuntu/Debian 安装依赖:
sudo apt-get install bubblewrap socat
然后在 Claude Code 中运行 /sandbox,选择 auto-allow 或 regular permissions。两种模式的隔离强度相同,区别只是沙箱内命令是否自动批准。
一份保守的项目配置
放在项目的 .claude/settings.json:
{
"permissions": {
"deny": ["Read(./.env)", "Read(./secrets/**)", "Bash(git push *)"]
},
"sandbox": {
"enabled": true,
"failIfUnavailable": true,
"autoAllowBashIfSandboxed": true,
"allowUnsandboxedCommands": false,
"filesystem": {
"denyRead": ["~/"],
"allowRead": ["."],
"denyWrite": ["/etc", "/usr/local/bin"],
"allowWrite": ["/tmp/project-build"]
},
"network": {
"allowedDomains": ["registry.npmjs.org", "api.anthropic.com"],
"deniedDomains": ["metadata.google.internal"]
}
}
}
failIfUnavailable: true 很重要:如果组织把 sandbox 当安全闸门,依赖缺失时应该启动失败,而不是警告后无沙箱运行。
路径规则最容易配错
Sandbox filesystem 使用常规路径语义:
| 写法 | 含义 |
|---|---|
/tmp/build | 文件系统绝对路径 |
~/cache | 用户主目录下路径 |
./output | 项目设置中相对项目根目录 |
. | 当前设置作用域的根目录 |
如果项目设置先 denyRead: ["~/"],再 allowRead: ["."],就能实现“默认不读 home,只重新放行当前项目”。如果同一配置写在用户级设置,. 会解析到 ~/.claude,不是任意项目目录。
网络白名单不是内容审计
内置代理根据目标 hostname 做允许判断,不终止 TLS,也不检查加密内容。因此:
- 放行
github.com仍可能提供数据外泄通道。 *.example.com的 blast radius 大于单个 API host。- 强威胁模型应使用自建代理终止 TLS、记录请求并检查内容。
- 不要允许 Docker socket;它等价于给沙箱访问宿主机的通道。
网络策略设计顺序:先列任务所需 endpoint,再逐个放行;不要从“允许互联网”开始做黑名单。
凭证边界
沙箱不是秘密管理器。项目应做到:
- 用短时 token,不把长期密钥写进仓库。
- 用权限规则和
denyRead隔离.env、SSH、云凭证。 - 为不同工具发最小权限身份。
- 通过外部代理或 broker 注入凭证,Agent 不直接看到真值。
- 审计每次外部写操作的身份、目标和 provider receipt。
如果命令必须访问 ~/.kube,优先精确加入 filesystem.allowWrite;不要把整个 kubectl 排除出沙箱。
excludedCommands 和逃生口
Docker、watchman 或某些系统工具可能无法在沙箱内工作。excludedCommands 会让匹配命令直接在沙箱外运行,风险应被明确记录。
allowUnsandboxedCommands: false 会关闭 dangerouslyDisableSandbox 逃生口。企业策略通常还要:
- 管理设置强制 sandbox enabled。
- 禁止 bypass permissions mode。
- 只接受 managed domain allowlist。
- 固定 MCP server allowlist。
验证,不要只看配置文件
| 测试 | 预期结果 |
|---|---|
| 在项目内创建临时文件 | 成功 |
写 /usr/local/bin | 被拒绝 |
读取被禁的 .env | 被拒绝 |
| 请求允许域名 | 成功 |
| 请求随机域名 | 被拒绝或触发权限流程 |
| 子进程尝试相同越界行为 | 同样被拒绝 |
| 移除 bubblewrap 后启动(强制模式) | 启动失败 |
把这些测试加入团队 onboarding 或 CI 环境镜像验收。配置存在,不等于实际运行时启用了隔离。
常见错误
| 错误 | 后果 | 修法 |
|---|---|---|
| 只配 permissions,不开 sandbox | 恶意子脚本绕过意图判断 | 加 OS 级运行时边界 |
| 只限制文件,不限制网络 | 读取到的数据可被外传 | 两层同时启用 |
| 放行过宽 wildcard | 白名单变成外泄跳板 | 精确到必要 host |
| 允许 Docker socket | 可控制宿主机 | 禁止 socket,另建隔离 runner |
| 沙箱不可用时继续运行 | 生产策略静默失效 | failIfUnavailable: true |
动手练习
为一个 Node.js 项目写 sandbox policy:只允许项目目录和 /tmp/build 写入,只允许 npm registry 与测试 API 出站。然后用主进程和 npm 子脚本分别尝试越界读、写、联网,保存实际拒绝结果。
自检
- 我能解释 permissions 与 sandbox 的不同执行时机。
- 文件系统和网络隔离都已启用。
- 凭证不直接暴露给生成代码。
- 沙箱不可用会 fail closed。
- 我测试了子进程,而不只测试 Claude 的直接命令。
相关阅读
参考资料
📚 相关资源
❓ 常见问题
点击问题,查看本章对应的实践答案。
沙箱和权限规则(permission rules)有什么区别?
权限规则在命令运行前根据命令字符串判断,防不住 npm install 里 postinstall 脚本干坏事;沙箱由操作系统(macOS Seatbelt / Linux bubblewrap)在运行时拦截进程本身,命令 spawn 的所有子进程都逃不出文件和网络边界。两者互补,不是替代关系。
开了沙箱是不是就不用管凭证安全了?
不是。默认读策略下 ~/.ssh/ 和 ~/.aws/credentials 仍然可读,环境变量也会原样继承给沙箱内命令。必须自己配 sandbox.credentials(deny 直接封禁,mask 用哨兵值替换、代理出站时注入真值)或设 CLAUDE_CODE_SUBPROCESS_ENV_SCRUB 才算保护到位。
Windows 上能用 Claude Code 沙箱吗?
原生 Windows 和 WSL1 都不支持。必须在 WSL2 里运行 Claude Code,并安装 bubblewrap 和 socat 两个依赖(sudo apt-get install bubblewrap socat);注意 WSL2 沙箱内无法调用 cmd.exe 等 Windows 二进制,需要的话加进 excludedCommands。
沙箱能完全防住数据外泄吗?
不能。内置代理默认只看目标域名、不解密 TLS 流量,放行 github.com 这类大域名后仍可能被 domain fronting 当外泄跳板。官方建议:威胁模型要求强保证时配自建代理做 TLS 终止 + 内容审计,白名单只放真正信任的域名。