跳到正文
码上手 coding.chengyu.dev

权限模式与安全:放手但不失控

确认弹窗点到第二十次,你就开始无脑点「是」了。这一课讲清六种权限模式、allow / ask / deny 规则的写法和 Bash 沙箱,以及怎样按场景在安全与效率之间取舍。

约 20 分钟 进阶 Claude Code 核实于 2026-10-25

学完这一课,你能

  • 能说出六种权限模式各自放行什么,并用 Shift+Tab 或启动参数切换
  • 能写出正确的 allow、ask、deny 规则,知道它们的判定顺序
  • 能说清沙箱管什么、不管什么,并在本机开启
  • 能为日常开发、陌生仓库和 CI 三种场景选出合适的配置

用 Claude Code 的人大致会经历两个极端。一开始,每条 pnpm test 都弹窗确认,点到第二十次,你已经不看内容直接回车了,这时的「确认」形同虚设。另一个极端是嫌麻烦,直接用 --dangerously-skip-permissions 全部放开,直到某天它顺手执行了一条你没想到的命令。

这两种都不对。Claude Code 提供了一整套分层的控制手段:权限模式定基调,权限规则做精细放行和拦截,沙箱在操作系统层面兜底。这一课把三者讲清楚。

权限确认:它在问你什么

默认情况下,不同类型的操作有不同的审批要求:

操作类型 例子 是否需要批准 选「是,且不再询问」的效果
只读 读文件、搜索 不需要(限工作目录内) 无
Bash 命令 运行 shell 命令 需要,内置只读命令除外 对该仓库和该命令永久生效
修改文件 编辑、写入 需要 本次会话结束前有效
网页抓取 WebFetch 需要,部分预批准的文档域名除外 对该仓库和该域名永久生效

ls、cat、grep、git status 这类内置只读命令不会弹窗。你选「是,且不再询问」时,规则会写进仓库根目录的 .claude/settings.local.json,以后在这个仓库里都生效。在弹窗里选中「是」或「否」后按 Tab,还可以附一句话给 Claude,比如「可以跑,但下次别加 --force」。

六种权限模式

模式 无需询问就能做 适合
default(界面上叫 Manual) 只有读取 敏感工作、陌生代码,每步都自己看
acceptEdits 读取、改文件、mkdir mv cp 等常见文件命令 一边让它改,一边在编辑器或 git diff 里审
plan 读取和探索,不改源码 动手前先摸清代码、出方案
auto 几乎所有操作,由后台分类器把关 长任务、减少打扰
dontAsk 只做读取和你预先批准的工具,其余一律拒绝 CI 和脚本
bypassPermissions 全部 仅限隔离的容器或虚拟机

切换方法:

  • 会话中按 Shift+Tab 循环切换,状态栏会显示 ⏸ manual mode on、⏵⏵ accept edits on、⏸ plan mode on、⏵⏵ auto mode on 等;
  • 启动时用参数,如 claude --permission-mode plan;
  • 设为默认在设置文件里写 permissions.defaultMode。

几个模式值得展开说:

auto 模式。 从 v2.1.283 起,只要你的账号和模型支持,交互式终端和 VS Code 会话的内置默认模式就是 auto(claude -p 等非交互运行默认仍是 Manual)。它不再逐条问你,而是由一个独立的分类器模型在执行前审查。默认会拦下的包括:curl | bash 式的下载即执行、把敏感数据发到外部、生产环境部署和迁移、强制推送、git reset --hard 这类丢弃未提交改动的命令、打印密钥等。同一操作连续被拦 3 次或累计 20 次,会暂停 auto、恢复弹窗。你在对话里说「先别 push」,分类器也会当成边界执行;但这句话如果在上下文压缩时被丢掉,约束就没了,所以真正的硬约束要写成 deny 规则。官方也明说:auto 模式减少弹窗,但不保证安全。

plan 模式。 Claude 可以读文件、跑探索性命令、写方案,但不改你的源码;方案确认前,编辑一直被拦着。也可以只给单条消息加 /plan 前缀。方案出来后可以选「是,并用 auto 模式」「是,手动批准每次编辑」或「否,继续规划」,按 Ctrl+G 能在编辑器里直接改方案。改动涉及多个文件、你对方案没把握时,先 plan 很值。

bypassPermissions 模式。 跳过几乎所有检查,等价于 --dangerously-skip-permissions。官方要求只在无法伤害宿主机的隔离环境里用,比如断网的容器或虚拟机。

权限规则:allow、ask、deny

模式定基调,规则做精细控制。规则写在设置文件的 permissions 下,格式是 工具名 或 工具名(说明符):

{
  "permissions": {
    "allow": [
      "Bash(pnpm test *)",
      "Bash(pnpm lint)",
      "Bash(git diff *)",
      "WebFetch(domain:vuejs.org)"
    ],
    "ask": [
      "Bash(git push *)"
    ],
    "deny": [
      "Read(./.env)",
      "Read(./.env.*)",
      "Read(./config/oss-keys.json)",
      "Bash(curl *)"
    ]
  }
}

这份配置的意思是:测试、lint、看 diff 不用问;推送前一定要问;不许读 .env 和存阿里云 OSS 密钥的文件,也不许用 curl。

规则的要点:

  • 判定顺序是 deny → ask → allow,第一个匹配的说了算,和写得多具体无关。 写了 deny: Bash(aws *),再写 allow: Bash(aws s3 ls) 也放不出来。
  • * 匹配任意文本,要放在子命令后面。 Bash(git log *) 只放行 git log,Bash(git *) 则放行所有 git 命令,包括 push。Bash(ls *) 不匹配 lsof,Bash(ls*) 会匹配。
  • 复合命令逐段匹配。 Bash(pnpm test *) 不会放行 pnpm test && rm -rf dist,每一段都要单独过关。
  • 路径写法有锚点。 在项目设置里,./ 或 / 开头相对于项目目录;// 开头是绝对路径;~/ 是家目录。注意 Read(/secrets/**) 写在用户设置里时指的是 ~/.claude/secrets/,不是你项目里的 secrets 目录。
  • Bash 参数级的限制很脆弱。 想限制 curl 只访问某个域名,换个参数顺序、换个协议就绕过去了。更可靠的做法是 deny 掉 curl,用 WebFetch(domain:...) 放行允许的域名,再配合沙箱的网络白名单。

规则放在哪个文件,决定了对谁生效:

文件 影响范围 适合放
~/.claude/settings.json 你在本机的所有项目 个人通用规则
.claude/settings.json 项目里所有人(提交到 git) 团队共享的规则
.claude/settings.local.json 只有你、只在这个项目 个人例外,弹窗里选的规则也存在这里
组织托管设置 组织部署到的所有机器 安全策略,其他层级无法覆盖

同名的普通配置项按优先级取值:托管设置 > 命令行 > 项目本地 > 项目共享 > 用户。但 allow、deny 这类列表会跨文件合并,并且任何一层的 deny 都没法被别的层的 allow 放开。另外,项目 .claude/settings.json 里的 allow 规则要等你在信任弹窗里确认这个文件夹之后才生效,deny 和 ask 则立即生效。随时可以用 /permissions 查看所有规则和它们来自哪个文件。

还有一类受保护路径,如 .git、.vscode、.husky、.claude 目录和 .zshrc、.npmrc、.mcp.json 等文件。写入它们不会因为 allow 规则或 acceptEdits 模式被直接放行:Manual 和 acceptEdits 下会弹窗,auto 模式下交给分类器审查,dontAsk 下直接拒绝,只有 bypassPermissions 会放行。

小测

你的 settings.json 里写了 allow: [「Bash(git *)」],deny: [「Bash(git push *)」]。Claude 执行 git push origin main 时会怎样?

沙箱:在操作系统层面兜底

规则是 Claude Code 自己执行的检查,沙箱则是交给操作系统强制的边界。运行 /sandbox 打开面板,选择模式即可开启:

  • 自动允许模式:在沙箱里跑的命令直接执行,不再弹窗。但 deny 规则照样拦截,Bash(git push *) 这类 ask 规则照样弹窗;
  • 常规权限模式:命令照样在沙箱里跑,但仍走正常的审批。

在面板里选的模式会存进项目的 .claude/settings.local.json,只对当前项目生效;想在所有项目里开启,在 ~/.claude/settings.json 里设置 sandbox.enabled 为 true。

开启后,Claude 运行的 shell 命令及其子进程默认:只能写工作目录、一个临时目录和你用 --add-dir 加入的目录;能读机器上的大部分文件(包括 ~/.ssh 这类凭据,需要用 denyRead 额外屏蔽);不能直接联网,所有连接都经过本机代理按域名白名单检查。白名单初始为空,命令第一次访问新域名时会询问你(auto 模式下由分类器审查),也可以像下面这样预先放行:

{
  "sandbox": {
    "enabled": true,
    "filesystem": {
      "denyRead": ["~/.ssh", "~/.aliyun"]
    },
    "network": {
      "allowedDomains": ["registry.npmmirror.com", "github.com"]
    }
  }
}

几个容易误会的地方:

  • 沙箱只包住 shell 命令。 Read、Edit、WebFetch 这些内置工具、MCP 服务器和 hook 都在沙箱外运行,它们由权限规则管。denyRead 拦不住 Read 工具,读 .env 的限制还得写成 Read 的 deny 规则。
  • 平台限制。 macOS 用系统自带的 Seatbelt,Linux 和 WSL2 需要先装 bubblewrap 和 socat;原生 Windows 不支持沙箱,要在 WSL2 里用。
  • 不走代理的工具连不上。 普通 ssh 和大多数数据库驱动不读代理变量,开了沙箱后,即使主机在白名单里,mysql 客户端这类工具也可能连不上,官方文档的排查部分有专门说明。
  • 有逃生口。 命令在沙箱里失败时,Claude 可能申请在沙箱外重跑。这次重跑不受沙箱保护,按你的权限模式审批:Manual 和 acceptEdits 下弹窗,auto 下交给分类器,bypassPermissions 下直接执行。想彻底关掉,在 sandbox 里设置 "allowUnsandboxedCommands": false。
  • 启动失败时默认不拦。 缺依赖或平台不支持导致沙箱起不来时,Claude Code 默认照常以非沙箱方式运行命令。把沙箱当安全底线的话,再设置 "failIfUnavailable": true,让它直接退出。

怎么取舍

场景 推荐组合
日常开发自己的项目 auto 模式,或 Manual 加沙箱自动允许;再用 allow 放行测试、lint 命令
刚 clone 的陌生仓库 先用 plan 模式读懂代码;Manual 模式,逐条审批
涉及生产配置、密钥、数据库 Manual 模式;把 .env、密钥文件、git push 写进 deny 或 ask
CI 和脚本 dontAsk 加精确的 --allowedTools,详见第 9 课
要完全无人值守 只在容器或虚拟机里用 bypassPermissions

原则只有一条:必须守住的底线写成 deny 规则或 hook,其余的交给模式和白名单去减少打扰。 CLAUDE.md 里写「不要读 .env」只是请求,不是保障;需要在某个时机强制执行的检查,交给第 7 课的 hook。

小测

你在 CLAUDE.md 里写了「绝对不要读取 .env 文件」,同时开启了沙箱并在 sandbox.filesystem.denyRead 里加了 .env。Claude 用内置 Read 工具读 .env 时会怎样?

动手:为你的项目配一份权限

生成权限配置草案 Claude Code

请为这个项目设计一份 .claude/settings.json 的 permissions 配置,先不要写入文件。

要求:

  1. 先读 package.json(或 pom.xml、Makefile 等)和 CI 配置,找出日常开发会反复执行的测试、lint、构建命令,放进 allow,通配符放在子命令之后;
  2. 找出项目里可能存放密钥或环境变量的文件(如 .env、各种 credentials、云服务密钥配置),为它们写 Read 的 deny 规则;
  3. git push、数据库迁移、发布脚本这类有外部影响的命令放进 ask;
  4. 对每条规则用一句话说明理由,并指出哪些规则更适合放在我个人的 settings.local.json 而不是提交给团队。

输出 JSON 和说明表格,等我确认后再写入。

动手清单

0/6

参考来源

工具更新很快,若与官方文档不一致,以官方为准。

学完了吗?

标记完成会记录在本机浏览器,计入你的学习进度和连续天数。

Esc

输入关键词开始搜索,例如「CLAUDE.md」「MCP」「部署」