权限模型:allowlist、只读模式、沙箱,安全与效率的平衡

一个必须先建立的认知

权限规则由 Claude Code(客户端)强制执行,而不是由模型自觉遵守。CLAUDE.md 里写"请不要删数据库"是行为引导,模型可能听也可能忘;permissions.deny 里的规则是硬边界,模型想越也越不过去。引导用文件,管控用规则——这句话是本章的总纲。

五种权限模式构成档位谱系

按"谁在审批"和"审批多严"排序:

模式 审批方式 典型场景
Manual(配置值 default) 每次改文件、跑命令都问你 敏感操作、不熟的代码库
Auto 后台分类器审查,放行安全动作、拦截危险动作 日常开发的默认档
Accept edits(acceptEdits) 文件编辑和 mkdir、mv、cp、sed 等常见文件命令自动放行,其他命令仍问 信任改动、把关命令
Plan 只读探索,不改文件 动手前审方案
Bypass(bypassPermissions) 全部放行 明确知道自己在干嘛的隔离环境

另有 dontAsk 模式:只允许预批准的工具,没预批准的直接拒绝而不是询问——适合无人值守场景。Shift+Tab 循环切换档位;启动时用 --permission-mode 指定,或在 settings.json 里设 permissions.defaultMode。注意 bypass 模式必须启动时显式开启,不能会话中途切进去;而且任何模式都不自动放行某些高危动作,权限不是无限松的橡皮筋。

Auto 模式自 v2.1.283 起是交互式会话的内置默认。它由一个分类器模型在后台审查动作,进 auto 模式时宽泛的"任意代码执行"白名单会被收窄,还有服务端探针扫描工具结果里的可疑内容——它是"效率档",不是"裸奔档"。

allowlist:把弹窗消天的正规手段

Manual 模式弹窗多,不是因为设计缺陷,而是因为你还没把可信命令写进白名单。规则写在 settings.json 的 permissions 字段:

{
  "permissions": {
    "allow": [
      "Bash(npm run *)",
      "Bash(git commit *)",
      "Read(~/.zshrc)"
    ],
    "deny": [
      "Bash(git push *)",
      "Read(.env)",
      "Bash(rm -rf *)"
    ],
    "ask": [
      "WebFetch"
    ]
  }
}

规则语法要点:Bash(npm run *) 的 * 通配;会话里弹窗时选"Yes, and don't ask again"就会自动写入这类规则;MCP 工具规则形如 mcp__服务器名__*。想偷懒的话,跑一次 /fewer-permission-prompts,它会扫描你的历史会话,找出常被放行的只读命令,生成一份白名单建议——相当于自动替你写了 allowlist。

规则求值顺序是硬性的:deny 先于 ask 先于 allow。 第一条命中的规则说了算,deny 无法被更具体的 allow 例外穿透。所以 deny 要写得克制——Bash(aws *) 会连 aws s3 ls 一起拦死。

三层作用域,团队与个人各得其所

权限规则可以放在不同层级的 settings 文件里:

  • 企业策略层(managed settings):IT 统一下发,个人不可覆盖;
  • 项目共享层(.claude/settings.json,进 git):团队共识,比如"本项目允许 pnpm test";
  • 个人本地层(.claude/settings.local.json,gitignore):你的个人偏好。

还有工作目录边界的概念:Claude 默认只能在启动目录(及 --add-dir 加的目录)内读写,目录外的文件访问要审批。输出重定向、tee 写文件的目标位置也会被检查,想用 > /etc/hosts 逃逸?没门。

权限弹窗里的交互细节

每天都在点的弹窗,其实有几个被大多数人错过的功能。批准时留言:在 Yes 或 No 上按 Tab 会展开一个备注框,你写的批注会连同选项一起交给 Claude——"可以,但别动迁移文件"这种一句话,能省掉一整轮返工对话。"不再问"的作用域:弹窗选项里的"don't ask again"写进的是当前作用域的设置文件,选之前想清楚这个命令是不是真的所有场景都可信。左右方向键可以切换弹窗的标签页(不同选项分组)。

还有一条对理解系统很有用:deny 和 ask 规则会穿透复合命令。Bash(git clean *) 在 ask 里,那 cd /tmp && git clean -f 也逃不掉——Claude Code 会拆解 &&、||、子 shell 里的嵌套命令逐段匹配。反过来 allow 规则比较保守:解析不了的命令宁可弹窗也不放行。设计取向很清晰:宁可麻烦你,不放可疑的过。

只读命令的自动判定

Claude Code 内置了一份"只读命令"清单(git status、ls、搜索类命令等),这些不需要配置就自动放行——所以 Manual 模式下你看到的弹窗基本都是写操作。解析失败的超长命令(超过一万字符)会保守地弹窗而不是猜。这份内置清单和你的 allow 规则是叠加关系,不用自己重复配置。

实战:为团队写一份权限基线

把前面所有知识收敛成一份可以直接抄的配置,放在项目 .claude/settings.json 里进版本库:

{
  "permissions": {
    "allow": [
      "Bash(npm run build)",
      "Bash(npm run test*)",
      "Bash(npm run lint*)",
      "Bash(git status)",
      "Bash(git log*)",
      "Bash(git diff*)"
    ],
    "deny": [
      "Read(.env*)",
      "Read(**/*secret*)",
      "Bash(git push *)",
      "Bash(rm -rf *)",
      "Bash(sudo *)"
    ]
  }
}

配套三个约定:.env 类文件永远 deny 读取(连转录里都不该出现它们的内容);push 由人执行;个人额外的白名单放自己的 settings.local.json 不进 git。这份基线让新同事第一天就能顺畅使用,同时把最危险的三类操作(泄密、误推送、误删除)物理锁死。

沙箱:给命令加一层物理隔离

规则匹配的是命令文本,理论上存在绕过写法(换路径调同一程序、sh -c 包一层)。当约束必须严格成立时,用沙箱:它从操作系统层面隔离命令的文件系统和网络访问。Linux、macOS 上原生支持,Windows 原生模式不支持(WSL 2 支持)。会话里跑 /sandbox 即可开关,配置里也有 sandbox.enabled。

组合使用的官方建议很实在:想限制网络访问,deny 规则拦 curl、wget 只是一半,配上网络白名单的沙箱才是完整方案。一个管"它想干什么",一个管"物理上能干什么"。

我的权限配置心法

带了几个团队后沉淀出的做法:

  1. 新项目起步:Manual + 跑一次 /fewer-permission-prompts,一天后就有顺手的白名单;
  2. 稳定期:Auto 模式 + deny 清单(.env、密钥目录、git push、危险命令);
  3. CI/无人值守:dontAsk + 精确 allowlist,或干脆上沙箱;
  4. 血泪经验:deny 里永远放 Read(.env*) 和密钥相关路径——不是不信任模型,是让"模型某天抽风读了密钥文件"这件事物理不可能发生。

权限配置调试有专门文档,规则没生效先跑 claude doctor 看解析错误。完整规则语法见官方权限文档。

本章由 WorkBuddy 原创整理,事实性内容参考 Claude Code 官方文档。