Claude Code 的沙箱到底隔离了什么:别把「有沙箱」当成「安全」

2026-08-18

先把问题问具体一点:你在一个会话里执行了 /sandbox,选了 auto-allow,然后让 Claude 跑构建、跑测试、装依赖。此刻如果这个仓库里藏了一段不怀好意的构建脚本,它能读到什么、能写到什么、能把数据发到哪里?

这个问题不能用「开了沙箱所以还好」回答。Claude Code 官方文档(code.claude.com/docs/en/sandboxing)在 Limitations 一节的开头就写明:沙箱降低风险,但不是一个完整的隔离边界(not a complete isolation boundary),在把它当成硬性安全控制之前要先看这些限制。下面就顺着文档描述的路径把这条边界走一遍。

第一层筛子:它只管 Bash 及其子进程

文档的 Scope 一节把范围划得很直白:沙箱隔离的是 Bash 子进程,其它工具在别的边界下运行。

  • 内置文件工具:Read、Edit、Write 直接走权限系统,不经过沙箱。
  • MCP 服务器与 hookssandbox-environments 页写明,它们是独立进程,在宿主上不受约束地运行(run unconstrained on the host)。
  • computer use:文档写明它跑在你真实的桌面上,而不是隔离环境里,由每个应用各自的权限提示来把关。
  • subagent:与父会话同进程、共用同一套沙箱配置,父会话开了沙箱,subagent 里的 Bash 命令也是沙箱内执行。

这一条是最容易被跳过的。很多人心里的「Claude Code 有沙箱」默认等于「整个会话被关在盒子里」,但按文档口径,盒子只罩住了 Bash 这一条腿。sandbox-environments 页给出的对照表里,只有 Sandboxed Bash tool 这一行的隔离对象是「Bash 命令及其子进程」,sandbox runtime、dev container、自定义容器、虚拟机这些才是把整个 Claude Code 进程装进边界。注意 sandbox runtime@anthropic-ai/sandbox-runtime)文档标明是 beta research preview,配置格式可能随包演进而变动。

第二层:文件系统隔离,默认「写得很窄,读得很宽」

沙箱有两个互相独立的层,文件系统隔离管路径,网络隔离管域名。文件系统这一层的默认值是不对称的:

  • 默认写:当前工作目录及其子目录,加上 $TMPDIR 指向的会话临时目录。
  • 默认读整台电脑,除了少数被拒绝的目录。文档在这里专门加了一句提醒——这个默认值依然允许读取 ~/.aws/credentials~/.ssh/ 这类凭据文件

第二条就是「有沙箱」和「安全」之间最大的那道缝。要堵上它,文档给的手段是 sandbox.credentials(需要 v2.1.187 或更高版本),或者把路径加进 sandbox.filesystem.denyRead。而且文档明说:没有内置的凭据拒绝清单(There is no built-in credential deny list),只有你列出来的文件和变量会被限制。你不列,它就不管。

credentials"mode": "deny" 有个细节值得记:文件路径的拒读属于文件系统层,环境变量的清除不属于。所以一旦你把 sandbox.filesystem.disabled 设成 true(关掉文件系统隔离、只保留网络隔离,需要 v2.1.216 或更高版本),文件那半边的保护就不生效了,环境变量那半边还在。文档为这个开关配了一段明确的警告:文件系统隔离关掉、命令又自动放行时,一个沙箱内命令可以写出后续命令会执行或读取的文件(shell 启动文件、$PATH 上的可执行文件、~/.claude/settings.json),从而在下一次运行时扩大自己的权限

{
  "sandbox": {
    "enabled": true,
    "filesystem": {
      "disabled": true
    },
    "network": {
      "allowedDomains": ["github.com", "*.npmjs.org"]
    }
  }
}

(以上配置原样取自官方文档 sandboxing 页的示例。以官方文档最新内容为准。)

protected paths:允许写的目录里,还有一批写不了

即便在沙箱准许写入的目录内部,仍有一批路径被拒写。文档自述了理由:能编辑这些文件的命令,可以给自己授予权限,或者加一个 Claude Code 会在沙箱之外运行的 hook 或 MCP 服务器。文档把这些路径分成四组

  1. 工作目录及其上级目录里的 .claude 设置文件、.claude/skills.claude/agents.claude/commands.claude/hooks 目录、.mcp.json,以及 Claude Code 自行运行的文件如 .claude/workflows.claude/scheduled_tasks.json
  2. 仅工作目录里的 shell 启动文件(如 .bashrc.zshrc)、.gitconfig.vscode.idea 目录,以及 .git 里的 hooksconfig
  3. 会把工作目录变成裸 git 仓库的那些文件(顶层的 HEADobjectsrefs,以及已存在时的 confighooks);
  4. ~/.claudeCLAUDE_CONFIG_DIR 指向的目录里的大部分内容,加上 ~/.claude.json.credentials.json 凭据存储。

关键在后半句:这些路径没有豁免机制。文档写明,allowWrite 条目或覆盖该路径的 Edit allow 规则都不会解除保护,唯一的关闭方式是 filesystem.disabled——而它是对所有路径一起关。想看这批路径在你机器上解析成了什么,文档写明可以运行 /sandbox 并打开 Config 标签页,其中大部分会列在 Denied within allowed 下面,和你自己写的 denyWrite 条目混在一起。

顺带一个实际会撞上的坑:文档在 Troubleshooting 里写明,git mergegit checkout 这类命令需要替换一个被拒写的文件时,会以 unable to unlink old 失败,Linux 与 WSL2 上错误结尾还会带 Read-only file system

第三层:网络隔离,以及代理默认不看 TLS

网络这一层由一个跑在沙箱之外的代理服务器控制。默认没有任何域名被预先允许,命令第一次需要某个新域名时会走审批;同意之后,该主机在本会话剩余时间内都被允许。想省掉这一步就用 allowedDomains 预先放行,WebFetch 的 allow 规则也会带入域名。

这一层最需要写实的是它的边界条件,文档在 Security limitations 里说得很清楚:内置代理基于客户端提供的主机名做放行判断,默认不终止也不检查 TLS,因此加密连接的内容不被检查。紧跟的警告写明,放行 github.com 这类宽域名可能形成数据外泄路径,沙箱内运行的代码有可能借助 domain fronting 之类的手法触达允许清单之外的主机;如果你的威胁模型需要更强的保证,要自建终止并检查 TLS 的代理,并把它的 CA 证书装进沙箱。文档同时注明,更强的 TLS 感知网络隔离仍处于活跃开发中(an active area of development)。

network.tlsTerminate 呢?文档标注它是 experimental(v2.1.199 及以后可用),作用是让内置代理自己终止 TLS,这是凭据 mask 替换所必需的——但文档同一句里写明,它并不增加内容过滤。别把它当成「开了就能看住流量」。

要把「提示」变成「直接拒绝」,文档给的是 strictAllowlist(需要 v2.1.219 或更高版本):设为 true 后,允许清单之外的主机对沙箱内命令直接拒绝而不是提示。两个限定要一起记:它只对沙箱内命令生效,WebFetch 这类进程内工具仍按各自的权限规则走;并且写在仓库的 .claude/settings.json.claude/settings.local.json 里无效,只有用户设置、managed 设置或 --settings 才认。

逃生舱与例外:文档自己列出的绕过路径

一条命令在沙箱里跑不起来时,Claude Code 会把违规详情附到失败输出后面,Claude 可能带上 dangerouslyDisableSandbox 参数重试。重试的命令跑在沙箱之外,因此回到常规权限流程。想彻底堵掉,文档给的是在沙箱设置里写 "allowUnsandboxedCommands": false——文档写明,/sandbox 的 Overrides 标签页把这个状态显示为 Strict sandbox mode,此时 dangerouslyDisableSandbox 参数被完全忽略。

组织侧的管控也有明确的缺口。文档写明:布尔键(如 enabledfailIfUnavailable)以 managed 值为准,开发者本地设的会被忽略;但数组键(如 excludedCommandsallowRead)是跨作用域合并的,开发者可以追加条目来放宽策略。allowRead 可以用 allowManagedReadPathsOnly 锁死,域名可以用 allowManagedDomainsOnly 锁死,而 excludedCommands 没有对应的 managed-only 锁定,文档的建议是把这份清单保持得窄一些。

还有几处文档明说会削弱隔离的开关,出现在你的配置里就该单独评估:allowUnixSockets 放行 /var/run/docker.sock 等于把宿主访问权交出去;enableWeakerNestedSandbox 让内层沙箱绑定挂载容器已有的 /proc,文档写明它会把本可被隐藏的进程信息暴露给沙箱内命令;macOS 上的 allowAppleEvents 一旦打开,文档写明它移除了代码执行隔离,沙箱内命令可以无提示地启动其它应用(受 macOS 自动化授权提示 TCC 约束)。

Windows 侧:没有原生支持这一说

这一段本站读者用得上:沙箱不支持原生 Windows,文档写明它运行在 macOS、Linux 和 WSL2 上,Windows 上要在 WSL2 发行版里跑 Claude Code。WSL1 不支持,因为 bubblewrap 需要只有 WSL2 才有的内核特性——文档写明可以在 PowerShell 里用 wsl -l -v 查版本,看到 Sandboxing requires WSL2 就说明当前发行版是 WSL1。

WSL2 上还有一个专门的行为:启动 cmd.exepowershell.exe/mnt/c/ 下的 Windows 二进制时,WSL 是通过一个 Unix socket 交给 Windows 宿主的,因此能不能启动取决于沙箱的 Unix socket 设置——文档写明,可选的 seccomp 过滤器必须先装上,才谈得上拦住这个 socket。要允许这类启动就设 allowAllUnixSockets,要让它完全绕开沙箱就把命令加进 excludedCommands

Linux 与 WSL2 侧还依赖两个包,文档给的安装命令是原样这一条(Ubuntu/Debian):

sudo apt-get install bubblewrap socat

依赖检查在启动时运行,装完包要重启 Claude Code,/sandbox 才会检测到。

所以「有沙箱」应该怎么读

把上面几条并在一起,文档自己给的结论其实很克制:sandbox-environments 页写明,单靠沙箱化的 Bash 工具不足以支撑完全无人值守的运行;使用 --dangerously-skip-permissions 时,一定要把会话放在容器、虚拟机或 sandbox runtime 里,让文件工具、MCP 服务器与 hooks 也在边界内。同一页的警告还提醒一件常被忽略的事:隔离不改变发给模型的内容,你的提示词和 Claude 读取的文件,无论有没有沙箱都会传给 Anthropic API 或你配置的供应商。

一个可操作的自查顺序:运行 /sandbox 看 Config 标签页解析出来的实际边界;运行 /doctor,它会报出 Sandbox network domain entries have unreliable spellings 这类拼写不可靠的域名条目;然后回头问自己——我的凭据文件在 credentials 里列了吗?我的 allowedDomains 有没有宽到能当外泄通道?我的 excludedCommands 里躺着哪些命令?

沙箱是一层防御,不是一个结论。它把「Claude 可能误删你 home 目录」这类事故的概率压下去了,但文档从头到尾都没有承诺它挡得住一个有意图的攻击面。


本文依据 Claude Code 官方文档(code.claude.com/docs)于 2026-08-17 的公开内容整理。 该产品闭源,本文只复述官方文档写明的机制,不推断其内部实现我们没有对文中涉及的功能做过实测,因此不涉及界面外观、操作手感与运行速度的任何描述。 该产品迭代频繁,文中涉及的命令、配置项与默认值随版本变动,请以官方文档最新内容为准。 本文不涉及价格、额度与限流的具体数值,相关信息请以官方定价与用量说明页为准。

安全与合规相关做法请结合自身环境评估,本文不构成安全方案建议。

想系统学会用 AI?报名体系课或加入会员,照着学、照着用。