Claude Code 学习站
Mingyu's Library

学习站 / 系统课程 / D3

D3 · 权限模式与沙箱

agentic loop 里每一次「行动」都要过授权这道闸门。本课讲清权限模式、allow/ask/deny 规则和沙箱各自管什么、怎么配合。

权限模式决定「多久问你一次」,权限规则决定「哪些操作预先放行或封死」,沙箱决定「命令跑起来之后能碰到什么」。三层各司其职,搭配得当,才能既少被打断又不裸奔。

为什么这天学这个

D1 里我们拆过 agentic loop:收集上下文 → 行动 → 验证,循环推进。其中「行动」这一环——编辑文件、跑 shell 命令、发网络请求——每一步都要先过 Claude Code 的授权闸门。不搞清楚它,你要么被弹窗打断到烦躁,要么图省事一路放行、把安全交给运气。

本课也是后面几课的地基:D5 要讲的 plan mode 本身就是一种权限模式;D13 的 Headless 与 CI 场景依赖 dontAsk 这类「永不等待输入」的模式。

概念讲清楚

权限模式:决定多久问你一次

官方当 Claude 想编辑文件、运行 shell 命令或发起网络请求时,它会暂停并请你批准;权限模式控制这个暂停出现的频率。官方共提供六种模式,每种在「便利」与「监督」之间做不同取舍:

模式免询问能跑什么适合场景
default(界面里叫 Manual)只有读取类操作上手阶段、敏感工作
acceptEdits读取、文件编辑,以及 mkdirtouchmvcp 等常见文件系统命令自己会 review diff 的日常迭代
plan读取;auto mode 可用时,还有 classifier 批准的命令改代码前先摸清代码库
auto几乎全部,由后台 classifier 逐个安全审查长任务、减少询问疲劳
dontAsk只有预先 allow 过的工具,其余直接拒绝锁死的 CI 与脚本
bypassPermissions全部只用于隔离的容器 / VM

官方几个细节:default 在界面里显示为 Manual,配置值仍写 default(v2.1.200 起 CLI 也接受 manual 别名);acceptEdits 的自动批准只覆盖工作目录和 additionalDirectories 内的路径;对受保护路径(.git.claude~/.zshrc 等)的写入,除 bypassPermissions 外任何模式都不自动批准。auto mode 需账号满足条件才会出现,其 classifier 会拦截超出请求范围、指向陌生基础设施、或疑似被恶意内容带偏的动作;bypassPermissions 则没有任何后台检查,不防 prompt injection,只应在容器、VM 等隔离环境使用。

怎么切换模式

官方模式通过这些控制切换,在聊天里「让 Claude 自己改」是无效的:

  • 会话中:按 Shift+Tab 循环 defaultacceptEditsplan,状态栏显示当前模式徽章(如 accept edits on)。auto 满足条件时加入循环;bypassPermissions 只有启动时用 --dangerously-skip-permissions 等 flag 启用后才进循环;dontAsk 从不进循环,只能启动时指定。
  • 启动时:传 flag,例如 claude --permission-mode plan,配 -p 的非交互运行同样适用。
  • 持久默认:在 settings 文件里设 permissions.defaultMode:
{
  "permissions": {
    "defaultMode": "acceptEdits"
  }
}

权限规则:allow / ask / deny 写在哪、怎么写

官方模式定的是基线,规则叠在模式之上做细粒度控制:allow 免询问放行,ask 每次强制询问,deny 直接禁止。用 /permissions 可以查看全部规则以及每条来自哪个 settings 文件。规则可以写在五级来源里,优先级从高到低:managed 托管设置 → 命令行参数 → .claude/settings.local.json(个人本地)→ .claude/settings.json(项目共享,可提交进 git)→ ~/.claude/settings.json(用户全局)。任何一级 deny 了某工具,其它级都无法再放行。

官方规则格式是 ToolTool(specifier)。Bash 规则支持 * 通配符,一个 * 能跨空格匹配多个参数;文件规则用 gitignore 语法;WebFetch 用 domain: 前缀;MCP 工具用 mcp__服务器__工具。一个典型的项目配置:

{
  "permissions": {
    "allow": [
      "Bash(npm run *)",
      "Bash(git commit *)",
      "WebFetch(domain:github.com)"
    ],
    "ask": [
      "Bash(git push *)"
    ],
    "deny": [
      "Read(./.env)",
      "Bash(curl *)"
    ]
  }
}

官方评估顺序是固定的:deny → ask → allow,第一个命中的规则决定结果,规则写得多具体都不改变这个顺序。所以宽的 Bash(aws *) deny 会盖过窄的 Bash(aws s3 ls) allow——deny 规则上打不了「例外的洞」。三类规则都没命中时,才落到当前权限模式的默认行为。另有一个内置只读命令集(lscatgreppwdgit 的只读形式等)在任何模式下都免询问。

工具调用 命中 deny 规则? 拦下,不执行 命中 ask 规则? 弹出询问,等你批准 命中 allow 规则? 免询问直接运行 都没命中:按当前权限模式决定 例如 default 会询问
权限决策流程:deny → ask → allow 依次匹配,第一个命中生效;全部未命中才由权限模式的基线接手。

官方在权限弹窗里选「Yes, don't ask again」,Bash 批准会永久存进仓库根的 .claude/settings.local.json,对该仓库未来会话生效(文件编辑的批准只活到会话结束)。另外,权限规则由 Claude Code 强制执行,不靠模型自觉——CLAUDE.md 里写「不要 git push」只影响 Claude 想不想,不改变能不能,这条边界 D4 还会用到。

沙箱:OS 级的硬边界

官方沙箱是内置在 Claude Code 里的操作系统级隔离:macOS 用系统自带的 Seatbelt,Linux 和 WSL2 用 bubblewrap(需要安装 bubblewrapsocat 两个包),原生 Windows 不支持。在会话里运行 /sandbox 打开面板即可启用,选择的模式保存到项目的 .claude/settings.local.json;想全局启用则在 ~/.claude/settings.json 里设 sandbox.enabled: true

官方沙箱有两层默认边界,由 OS 对 Bash 命令及其所有子进程强制执行:

  • 文件系统:默认只能写工作目录和会话临时目录;读取默认几乎全盘可读——注意 ~/.ssh~/.aws/credentials 等凭据默认仍读得到,要挡需配 sandbox.credentialsdenyRead
  • 网络:走沙箱外的代理,默认零预允许域名;命令第一次要连新域名时弹批准,同意后本会话内记住(v2.1.191 起),也可用 allowedDomains 预先放行。

官方沙箱自身有两种审批模式:auto-allow(能进沙箱的命令免询问直接跑)和 regular permissions(照常走权限流程),两者隔离边界完全相同,差别只在是否自动批准。即便 auto-allow,deny 规则、内容级 ask 规则(如 Bash(git push *))、指向 / 或 home 的 rm 仍会拦。命令因沙箱限制失败时,Claude 可能带 dangerouslyDisableSandbox 参数在沙箱外重试并走常规权限流程;设 allowUnsandboxedCommands: false 可关掉这个逃生舱。

官方划重点:沙箱只覆盖 Bash 命令及其子进程。Read、Edit、WebFetch 走权限系统,MCP 服务器和 hooks 是宿主机上不受约束的独立进程。要把整个进程都关进边界,官方还提供 sandbox runtime(无需 Docker)、dev container、自建容器、VM,以及 Anthropic 托管 VM 的 Claude Code on the web,隔离强度与搭建成本递增。

权限和沙箱是什么关系

官方两者是互补的两道闸门,拦的东西不同:权限在命令运行前评估,依据是命令字符串(auto mode 下再加一层 classifier 判断);沙箱在命令运行时由操作系统在进程上强制,哪怕被放行的命令「名不副实」,或 Claude 被文件里的恶意内容 prompt injection 带偏,OS 边界依然拦得住。

一条 Bash 命令 第一道:权限规则 运行前,看命令字符串 deny → ask → allow 第二道:沙箱 运行时,OS 强制 文件与网络边界 权限拦「能不能跑」,沙箱拦「跑起来能碰什么」;沙箱只覆盖 Bash 及其子进程
defense-in-depth:第一道闸被绕过或判断失误时,第二道 OS 边界仍然生效。

官方两层配置还会合并:sandbox.filesystem 与 Read/Edit deny 规则合并成最终文件边界,WebFetch 域名规则与 allowedDomains/deniedDomains 合并成网络边界。开沙箱且 autoAllowBashIfSandboxed 保持默认 true 时,沙箱化命令即使有裸 Bash ask 规则也免询问——沙箱边界替代了那次整工具级询问。

安全与效率怎么取舍

本站观点取舍的核心原则只有一句:每放松一层「询问」,就要有另一层「硬边界」顶上。询问是最后的人肉防线,拿掉它之前,先确认 classifier、沙箱或容器至少有一个在场。按场景对照:

  • 陌生仓库、敏感操作:Manual 或 plan 模式,逐条过目,慢就是快。
  • 日常迭代、事后 review diff:acceptEdits + 沙箱 auto-allow。文件编辑和工作目录内的命令都不打断,出圈的操作(新域名、git push、受保护路径)照样询问。
  • 长时间无人值守:auto mode(classifier 逐动作兜底),或在容器 / VM 里跑 bypassPermissions——官方明确后者没有任何检查兜底,隔离环境不是建议而是前提。
  • CI 流水线:dontAsk + permissions.allow 白名单,永不等待输入,名单之外一律拒绝。

本站观点常见误区:把「开沙箱」当成一种权限模式。它不是——模式决定是否询问,沙箱决定能碰什么,两者独立开关、可任意组合。想系统性减少询问而不牺牲安全,配方见 Tip 《怎么让权限少打断我》

当天能做完的实操

以下步骤在自己任意一个 git 项目里做,30 分钟内能跑完:

  1. 认识模式循环:启动 claude,连按 Shift+Tab。预期:状态栏在 Manual → accept edits onplan mode on 之间循环;账号满足条件时还会出现 auto mode on
  2. 查看现有规则:输入 /permissions。预期:看到 Allow / Ask / Deny 三类规则列表,以及每条规则来自哪个 settings 文件。
  3. 写两条规则:在项目根新建或编辑 .claude/settings.json,放入上文「权限规则」小节的 JSON(可按自己项目替换命令)。预期:重启会话后首次会弹 workspace trust 对话框列出这些 allow 规则,接受后 npm run build 类命令直接放行,git push --dry-run 命中 ask 规则强制弹窗。
  4. 体验「不再询问」:让 Claude 跑一条无害但不在只读集的命令(如 npm --version),在弹窗里选「Yes, don't ask again」。预期:.claude/settings.local.json 里多出一条对应 allow 规则,未来会话直接放行。
  5. 开沙箱:运行 /sandbox,在 Mode 页选 auto-allow(macOS 开箱即用;Linux/WSL2 若面板只显示 Dependencies 页,先 sudo apt-get install bubblewrap socat 再重启)。预期:Claude 在工作目录内跑写文件的命令不再弹询问;访问一个从没允许过的域名,会弹出域名批准。
  6. 整体验证:把下面的 prompt 原样贴给 Claude Code:
帮我验证当前会话的权限配置,依次执行:
1. 运行 ls 和 git status(内置只读命令,应该不询问);
2. 运行 npm run build(如果我配了 allow 规则,应该直接放行);
3. 尝试 git push --dry-run(应该命中 ask 或 deny 规则,被询问或拦下)。
做完后逐条告诉我:每条命令分别走了哪条路径——
内置只读集、allow 规则、ask 规则,还是模式默认询问。

预期:三条命令的授权表现各不相同,Claude 的复盘能和你在 /permissions 里看到的规则一一对上。

验收标准

  • 能不看文档说出六种权限模式的名字,以及每种「免询问能跑什么」。
  • 能解释 deny → ask → allow 的评估顺序,并说清为什么宽 deny 不能被窄 allow 打洞。
  • 能用一句话讲清权限与沙箱的分工:权限在运行前决定能不能跑,沙箱在运行时用 OS 限制能碰什么,且沙箱只覆盖 Bash 及其子进程。
  • 实操跑通:/permissions 里能看到自己写的规则;沙箱 auto-allow 下工作目录内命令免询问、新域名会弹批准。
  • 自测题:同时存在 allow 规则 Bash(aws s3 ls) 和 deny 规则 Bash(aws *),Claude 跑 aws s3 ls 会怎样?(答案:被拒绝——deny 先于 allow 评估,且规则具体程度不改变顺序。)