Claude Code 权限与安全:模式、沙箱与 settings.json
Posted on Sun 27 September 2026 in AI 编程 • Sucre Liu
先分清两件事
Claude Code 的安全模型有两层,经常被混为一谈:
- 权限模式决定"要不要问你就做";
- 隔离边界(沙箱、容器)决定"做的时候能碰到什么"。
模式再紧,命令一旦跑起来还是能碰到它能碰到的;隔离再强,模式不对你也会被频繁打断。两层要分开设计。
六个模式,逐档看
配置值用括号标注,CLI 里按 Shift+Tab 循环切换:
| 模式 | 不问就执行 | 适合 |
|---|---|---|
Manual(default) |
只读 | 敏感代码、陌生仓库 |
acceptEdits |
读 + 改文件 + 常见文件系统命令 | 你打算事后看 git diff |
plan |
只读 + 分类器放行的命令 | 动手前先出方案 |
auto |
几乎全部,后台分类器把关 | 长任务、减少打断 |
dontAsk |
只读 + 预先批准的工具,其余一律拒绝 | 锁死的 CI/脚本 |
bypassPermissions |
全部 | 仅限隔离容器/虚拟机 |
几个容易记错的点:
- Manual 并不禁止读。读不改变任何东西,所以任何模式都放行。
- 模式是基线,权限规则叠在上面。deny 规则在任何模式都生效,包括
bypassPermissions。 - 反过来,allow 规则在
bypassPermissions下没有意义——因为它本来就全放。
任何模式都不会自动放行的动作
这是很多人误以为"开了 auto 就万事大吉"的地方。以下动作在所有模式下都不会被自动批准:
- 被显式
ask规则命中的工具; - 需要用户交互的工具(比如它向你提问的那个);
- 针对关键路径的
rm/rmdir(如rm -rf /、rm -rf ~); - 开启
blockReadsOutsideWorkingDirectories后,对工作目录外路径的读取。
也就是说,有些闸门是设计上焊死的,切换模式也绕不过去。这是好事。
acceptEdits 到底放行了什么
这档的自动化范围比字面意思广一些。它在工作目录内会自动批准:
- 文件创建与编辑;
- 一组常见文件系统命令:
mkdir、touch、rm、rmdir、mv、cp、sed; - 这些命令带安全前缀(如
timeout、nice、nohup、LANG=C)也算。
但工作目录之外的路径、受保护路径、关键路径的删除、以及其余所有 Bash 命令,仍然会问。所以 acceptEdits 适合"我信任它在项目里改,改完我自己看 diff",不适合"我不在乎它跑什么"。
plan 模式:把方案当成可审查的产物
Shift+Tab 切换到 plan,或给单条提示加 /plan 前缀。它会读文件、跑探索命令、写方案,但不改你的源码。
方案出来后会给你三个选择:
- 是,并用 auto:批准并进入 auto 模式开始改;
- 是,逐条批准:批准但每次改动仍问你;
- 不,继续规划:留在 plan 里,告诉它改哪里。
有个不太被注意的功能:按 Ctrl+G 可以把方案拉到你的默认编辑器里直接改,再让它执行。对复杂任务,先改方案比事后改代码便宜得多。
auto 模式:分类器挡了什么
auto 模式用一个独立的分类器模型在后台审每个动作,替你把关,减少打断。它默认信任你的工作目录和会话开始时已配置的远程,其余视为外部。默认拦截的典型动作包括:
curl | bash这类"下载并执行代码";- 向外部端点发送敏感数据;
- 生产环境部署与迁移;
- 云存储上的批量删除;
- 授权 IAM 或仓库权限;
git push --force、git reset --hard、git clean -fd;- 对已在会话外推送过的提交做
--amend; terraform destroy/pulumi destroy这类销毁式操作;- 未获批就合并 PR、关闭 CI 检查;
- 把凭据/令牌打印进记录或文件。
但官方的警告必须照抄一遍:auto 减少打断,但不保证安全。 它适合"大方向我信得过"的任务,不能替代你对敏感操作的审查。而且它只在满足条件时可用:支持的套餐、模型(如 Sonnet 4.6+、Opus 4.6+)和 provider;组织也可以直接禁用。
把规则固化进 settings.json
模式是临时选择,规则是长期资产。常用的两个方向:放行可信命令、禁掉危险路径。
放行 npm test、git status 这类不需要每次确认:
{
"permissions": {
"defaultMode": "default",
"allow": ["Bash(npm test)", "Bash(git status)", "Bash(git diff *)"],
"deny": ["Read(./.env)", "Read(./secrets/**)", "Bash(curl *)"]
}
}
几个必须知道的细节:
- deny 是硬的:它在所有模式生效,
bypassPermissions也拦得住(少数内置交互工具除外)。 defaultMode: "auto"不认项目级设置。如果你发现设了 auto 却还是 Manual,多半是写在了.claude/settings.json;把它移到~/.claude/settings.json。bypassPermissions同样不认项目级设置,写在项目里会退回 Manual。这是防止仓库"自我放权"的设计。
配置作用域从组织托管策略到个人偏好,优先级由近到远。写敏感放行规则前,先想清楚它会不会被提交给团队。
硬拦截:钩子
settings.json 的允许/拒绝列表能覆盖大部分情况,但有些规则想带条件、带提示语,就该上钩子。一个最小的"保护敏感文件"示例——拦截对 .env 的编辑,并把原因回给模型:
#!/bin/bash
# .claude/hooks/protect-files.sh
INPUT=$(cat)
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')
FILE_PATH="${FILE_PATH//\\//}"
for pattern in ".env" "package-lock.json" ".git/"; do
if [[ "$FILE_PATH" == *"$pattern"* ]]; then
echo "Blocked: $FILE_PATH 命中保护模式 '$pattern'" >&2
exit 2 # exit 2 = 拦截,stderr 作为反馈给模型
fi
done
exit 0
注册为 PreToolUse,匹配 Edit|Write:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{ "type": "command", "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/protect-files.sh" }
]
}
]
}
}
注意 exit 2 的语义:拦截动作,并把 stderr 作为反馈交给模型,它会据此换一条路走——这正是"拒绝"和"无声失败"的区别。钩子的完整用法放在下一篇。
沙箱与容器
权限模式管"问不问",沙箱管"能碰到什么"。内置的沙箱 Bash 工具在 macOS/Linux/WSL2 上提供文件系统与网络隔离;更彻底的隔离是容器或虚拟机。选隔离级别,看你的威胁模型:
- 只是怕误删/误提交 → 模式 + 规则 + 检查点就够;
- 要跑不可信的自动化 → 上容器,并且以非 root 用户运行。
bypassPermissions 只在最后那种场景里出现,而且要配合容器。裸机裸仓库用它,是把保险丝换成铜丝。
检查点救不了的事
再提醒一次第 1 篇讲过的边界:Esc Esc 的检查点只能回退文件改动,而且不覆盖符号链接/硬链接。影响远端系统的动作——数据库、API、部署——无法回退。 那些只能靠模式和规则提前挡住。
所以这份安全配置的优先级是:
- 敏感操作不上
bypassPermissions; - 用
deny把秘密文件和危险命令焊死; - 用钩子处理需要条件和反馈的规则;
- 剩下的用模式控制打断频率。
小结
- 模式定基线,规则叠在上面;deny 在所有模式生效,allow 在 bypass 下无效。
- 关键路径
rm、交互式工具等,永远不自动放行。 acceptEdits会连带放行一批文件系统命令,注意范围。auto挡的是经典危险动作,但不等于安全。- 项目级设置无法开启
auto/bypassPermissions,这是有意的。
下一篇进入扩展层:CLAUDE.md、Skill、Subagent、Hook、MCP 到底该怎么选,以及各自最小的可运行示例。