权限模式与安全:放手但不失控
确认弹窗点到第二十次,你就开始无脑点「是」了。这一课讲清六种权限模式、allow / ask / deny 规则的写法和 Bash 沙箱,以及怎样按场景在安全与效率之间取舍。
学完这一课,你能
- 能说出六种权限模式各自放行什么,并用 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/settings.json 的 permissions 配置,先不要写入文件。
要求:
- 先读 package.json(或 pom.xml、Makefile 等)和 CI 配置,找出日常开发会反复执行的测试、lint、构建命令,放进 allow,通配符放在子命令之后;
- 找出项目里可能存放密钥或环境变量的文件(如 .env、各种 credentials、云服务密钥配置),为它们写 Read 的 deny 规则;
- git push、数据库迁移、发布脚本这类有外部影响的命令放进 ask;
- 对每条规则用一句话说明理由,并指出哪些规则更适合放在我个人的 settings.local.json 而不是提交给团队。
输出 JSON 和说明表格,等我确认后再写入。
动手清单
0/6参考来源
- Choose a permission mode(Claude Code 官方文档)
- Configure permissions(Claude Code 官方文档)
- Settings files and precedence(Claude Code 官方文档)
- All settings(Claude Code 官方文档)
- Configure the sandboxed Bash tool(Claude Code 官方文档)
工具更新很快,若与官方文档不一致,以官方为准。
学完了吗?
标记完成会记录在本机浏览器,计入你的学习进度和连续天数。