Claude Code:第 7 章 权限、安全与撤销
Claude Code:第 7 章 权限、安全与撤销
摘要:让 AI 直接改磁盘、跑命令,安全边界就成了第一问题。本文讲透六种权限模式的适用场景,规则语法的求值顺序与空格陷阱,沙箱默认不保护哪些目录,以及检查点回滚的能力边界。核心结论只有一句:权限管“能不能做”,检查点管“能不能撤”,两者都不能替代 git。
关键词:权限模式;规则语法;沙箱;检查点回滚
适用版本:Claude Code 2.1.x | 文中参数、配额与价格随版本变化,以官方最新说明为准
7.1 两层安全机制
Claude Code 有两个互不替代的机制:
| 机制 | 管什么 | 能撤吗 |
|---|---|---|
| 权限(permissions) | 做之前要不要问你 | — |
| 检查点(checkpoints) | 做了之后能不能撤销文件改动 | 能,但只管文件 |
只有权限能防止「不该发生的事」。检查点不能替代 git。
7.2 六种权限模式
📘 官方:权限模式决定「哪些动作不用问你就能做」。
| 模式 | 配置值 | 不用问就能做 | 适合 |
|---|---|---|---|
| Manual | default |
只读 | 逐条审阅、敏感工作 |
| Accept edits | acceptEdits |
读、改文件、常见文件系统命令(mkdir touch rm rmdir mv cp sed) |
边改边审代码 |
| Plan | plan |
读;auto 模式可用时还有分类器批准的命令 | 改代码前先探索 |
| Auto | auto |
全部,但后台分类器检查 | 长任务、减少弹窗疲劳 |
| Don’t ask | dontAsk |
工作目录内的文件读取 + 预批准的工具;其余一律拒绝 | 锁定的 CI / 脚本 |
| Bypass | bypassPermissions |
全部 | 仅限隔离容器/虚拟机 |
🔍 补充细节:
default在 CLI、--help、VS Code、JetBrains、桌面端里都显示为 Manual。配置值仍是default(hooks 和 SDK 用这个),CLI 也接受manual作为别名。
切换
CLI 里按 Shift+Tab 循环:
auto → default → acceptEdits → plan →(可选模式)→ auto
状态栏会显示 ⏸ manual mode on、⏵⏵ accept edits on、⏸ plan mode on、⏵⏵ auto mode on、⏵⏵ don't ask on、⏵⏵ bypass permissions on。
不是所有模式都在循环里:
| 模式 | 什么时候进循环 |
|---|---|
auto |
自动模式可用时 |
bypassPermissions |
你先用 --permission-mode bypassPermissions、--dangerously-skip-permissions、--allow-dangerously-skip-permissions 启动,或在用户/--settings/managed settings 里设了 defaultMode |
dontAsk |
永远不在循环里,只能 --permission-mode dontAsk |
--allow-dangerously-skip-permissions 的特殊之处:把模式加进循环但不激活它。
启动时的模式(决定顺序)
新会话按下面顺序取第一个生效的:
--permission-mode或--dangerously-skip-permissions- settings 文件里的
permissions.defaultMode - 内置默认
内置默认:
| 情况 | 默认模式 |
|---|---|
任何 settings 文件设了 disableAutoMode: "disable" |
default |
claude -p 或 Agent SDK |
default |
| 组织启用了 HIPAA 配置且会话符合条件 | default(auto 仍可切过去) |
| 终端会话 / VS Code 扩展 | auto(v2.1.283+;更早版本仅 Pro/Max/Team) |
⚠️ 踩坑提醒(高频困惑):
- 在
.claude/settings.json或.claude/settings.local.json里写"defaultMode": "auto"不生效——项目级文件里的auto会被忽略,回落到内置默认。要设 auto 就写在~/.claude/settings.json。- 在项目级文件里写
"defaultMode": "bypassPermissions"也不生效,会话会以 Manual 启动。
// ~/.claude/settings.json
{
"permissions": { "defaultMode": "default" }
}
🔍 补充细节:很多人抱怨「我明明设了 default 怎么还是 auto」——很可能是设在了项目文件里,或者优先级被 managed settings 覆盖。用
/status和/permissions核对实际生效值。
7.3 哪些动作任何模式都不会自动批准
📘 官方:即使在
bypassPermissions下,下面这些也不会自动放行:
- 被显式
ask规则匹配的工具- 组织把 connector 工具设为
ask的- 需要用户交互的工具:
AskUserQuestion、标了requiresUserInteraction的 MCP 工具- 被识别为关键路径的
rm/rmdir(如rm -rf /、rm -rf ~)——任何 allow 规则或 PreToolUse hook 的"allow"都批准不了- 跨会话消息的安全保护
- 开了
permissions.blockReadsOutsideWorkingDirectories时,工作目录外的读取
受保护路径(protected paths)
.git、.claude 这类路径的写入永不自动批准——除非在 bypassPermissions 模式,或者在有 bypass 可用的交互式终端里处于 plan mode。
7.4 权限规则语法
规则格式是 Tool 或 Tool(说明符)。
基本形态
| 规则 | 效果 |
|---|---|
Bash |
所有 Bash 命令 |
Bash(*) |
同上 |
Read |
所有文件读取 |
WebFetch |
所有网页抓取 |
Bash(npm run build) |
精确匹配这一条命令 |
Bash(npm run *) |
npm run 开头的所有命令 |
Read(./.env) |
读当前目录的 .env |
WebFetch(domain:示例域名) |
抓取示例域名 |
mcp__* |
所有 MCP 工具(仅 deny/ask 可用) |
mcp__puppeteer__* |
puppeteer 服务器的所有工具(allow 里也可用) |
🔍 补充细节:作为 deny 规则的裸工具名(如
Bash)会把工具从 Claude 的上下文里彻底移除——它根本看不到这个工具。带说明符的规则(Bash(rm *))则保留工具,只是在调用时才拦。这个区别在排查「它怎么不用某个工具」时很有用。
求值顺序(背下来)
📘 官方:deny → ask → allow。按这个顺序取第一个匹配,规则的具体程度不影响顺序。
含义很关键:
Bash(aws *)(deny)+Bash(aws s3 ls)(allow)→ aws 全部被禁,allow 无法「开洞」Bash(git push *)(ask)+ 更宽的 allow → 仍然会提示
通配符的三个匹配规则
📘 官方:
*代替它所在位置的任意文本。 在Bash(git * main)里它代替子命令,所以匹配所有 git 子命令和之前的选项(包括-c);在Bash(* --version)里它代替程序名。- 结尾的
*且前面有空格时,也匹配裸命令。Bash(ls *)匹配ls;但前提是这个结尾*是规则里唯一的通配符——Bash(* --help *)匹配npm --help x但不匹配npm --help。 - 结尾
*前面的空格是规则的一部分。Bash(ls *)要求ls后有空格,所以不匹配lsof;Bash(ls*)没有空格,匹配lsof。
| 你写的 | 匹配 | 不匹配 |
|---|---|---|
Bash(npm run build) |
npm run build |
npm run build --watch |
Bash(npm run *) |
npm run build、npm run test --watch、npm run |
npm install |
Bash(git log * main) |
git log --oneline main、git log -5 main |
git log main、git push origin main |
Bash(git * main) |
git merge main、git push origin main |
git log |
Bash(* --version) |
node --version、bash -c 'echo hi' --version |
node -v |
Bash(ls *) |
ls -la、ls |
lsof |
Bash(ls*) |
ls -la、lsof |
|
Bash(* --help *) |
npm --help x |
npm --help |
⚠️ 踩坑提醒:
*要放在子命令之后。git log --oneline main里git是程序、log是子命令。Bash(git *)放行所有 git 命令;Bash(git * main)会在启动时警告「通配符放在了子命令前面」,因为它放得太宽。
Bash(ls:*) 与 Bash(ls *) 等价。但 :* 只在结尾才被识别;Bash(git:* push) 里的冒号是字面字符,匹配不到任何 git 命令。
按输入参数匹配(deny / ask 专用)
{
"permissions": {
"ask": [
"Agent(model:opus)",
"Bash(run_in_background:true)",
"Bash(dangerouslyDisableSandbox:true)"
]
}
}
规则:
- 参数必须是工具输入的直接字段,嵌套在对象/数组里的不行
- 一条规则只写一个参数;要同时卡 model 和 isolation 就写两条
- 值支持
*通配,不写*就是精确匹配 - 模型省略的参数永远不匹配(
Agent(model:*)不匹配没设 model 的调用) - 值跟 Claude 实际发送的字面值比较,不做归一化(
Agent(model:opus)匹配别名opus,不匹配完整模型 ID) - 不能用这种方式匹配工具的主要内容字段:Bash 的
command、Read/Edit/Write 的file_path、Grep/Glob 的path、WebFetch 的url。Bash(command:rm *)会被忽略并给启动警告——用Bash(rm *)代替
完整配置示例
{
"permissions": {
"allow": [
"Bash(npm run *)",
"Bash(git commit *)",
"Bash(git status)",
"Bash(git diff *)",
"Edit",
"Read"
],
"ask": [
"Bash(git push *)"
],
"deny": [
"Bash(rm -rf *)",
"Read(./.env)",
"Read(./secrets/**)",
"WebFetch(domain:evil.example)"
],
"defaultMode": "acceptEdits",
"additionalDirectories": ["../shared-lib"]
}
}
「Yes, and don’t ask again」保存在哪
📘 官方:Bash 命令和 WebFetch 域名的永久批准会写进仓库根目录的
.claude/settings.local.json(通过 worktree 解析到主检出),对该仓库任何位置的后续会话生效。
文件修改的批准不落盘,只在本次会话内有效。
7.5 管理权限:/permissions
/permissions
这个对话框列出所有规则以及每条规则来自哪个 settings 文件。Claude 干活时也能打开;增删规则会从下一次工具调用就生效。
🔍 补充细节:排查「为什么它问了我一个我以为已经放行的命令」时,
/permissions是唯一能看清全貌的地方——包括项目级、用户级、managed 各层的规则。
给权限提示加备注
在大多数权限提示上,移到 Yes 或 No 按 Tab 打开备注框:
- Enter 提交答案并附备注
- Tab 关闭备注框但不作答
- 选 Yes → 执行后把备注发给 Claude
- 选 No → 备注作为拒绝原因发给 Claude,它继续工作
🔍 补充细节:这是个被忽略的高效技巧。拒绝时写「不要动 migrations 目录,改 config/ 下的 schema」比直接按 No 有用得多。
7.6 Auto 模式:让分类器替你值班
它做什么
📘 官方:一个单独的分类器模型在动作执行前审查,拦截三类:超出你请求范围的升级、针对未识别基础设施的动作、由它读到的敌意内容驱动的动作。
默认会被拦的典型动作(节选):
- 下载并执行代码(
curl | bash) - 把敏感数据发到外部端点
- 生产环境部署 / 迁移
- 云存储上的批量删除
- 授予 IAM 或仓库权限
- 修改共享基础设施
- 销毁会话开始前就存在的文件
- force push
- 提交或推送会泄露密钥/敏感数据的改动
git reset --hard、git checkout -- .、git restore .、git clean -fd、git stash drop/clear- 修改不是本会话创建的 HEAD 提交(
--amend) terraform destroy/pulumi destroy/cdk destroy/terragrunt destroy- 写 secret manager、改 DNS / TLS 证书
- 合并未经人工批准的 PR、批准 Claude 自己的 PR、关闭 CI 检查
- 开关/删除生产 feature flag
- 打印真实凭证到对话或文件
- 绕过内部包仓库走公共源
- 用
--insecure之类关闭安全护栏的参数 - 启动无人审批、无沙箱的自治循环(比如带
--dangerously-skip-permissions的进程) - 通配符 / glob / 按时间删除
/tmp、$TMPDIR等共享临时目录里的文件 - 把敏感细节写进发给他人的内容(PR 正文、issue、提交信息、评论)
- 注释掉、删除或强行通过保护安全行为的测试/断言
- 把 API base URL、代理端点、webhook、registry 镜像改为第三方主机
- 改动 push 去向(
git remote set-url/git remote add),除非你点名了新 remote - 向已知公开的仓库推送密钥或个人数据
- 向 Claude Code 自己的 tmux pane 发送按键(视为改自己的权限)
默认放行的典型动作:
- 工作目录内的本地文件操作
- 安装 lock 文件 / manifest 里声明的依赖
- 读
.env并把凭证发给它对应的 API - 只读 HTTP 请求
- 向你正在工作的这个仓库的任意分支 push(包括默认分支)——除非分支名表明它是部署/发布目标,如
production、gh-pages - 删除本会话自己创建的那些 job
- 为完成任务而读写安全相关代码、配置、威胁模型
- 同一个多代理会话内代理之间的消息
- 发数据到你在
environment里列出的可信域名/桶/服务 - 在 Chrome 里导航到可信内网域名、本地主机、或你点名的 URL
⚠️ 踩坑提醒:auto 模式降低弹窗,不保证安全。官方原话:“Use it for tasks where you trust the general direction, not as a replacement for review on sensitive operations.”
可用条件
| 维度 | 要求 |
|---|---|
| 套餐 | 所有套餐 |
| 组织 | Team/Enterprise 默认可用;管理员可用 disableAutoMode: "disable" 关掉 |
| 模型(Anthropic API、Claude Platform on AWS) | Opus 4.6+、Sonnet 4.6+、Haiku 5.5、Fable 系列 |
| 模型(Bedrock / Google Cloud / Foundry / 已登录网关) | 只有 Sonnet 5+、Opus 4.7+、Haiku 5.5、Fable 系列 |
| 不支持的模型 | Sonnet 4.5、Opus 4.5、Haiku 4.5、claude-3 系列 |
出问题时
- 报「auto mode unavailable」→ 先查上面的条件,再查有没有 settings 文件设了
disableAutoMode - 报某个动作「cannot determine the safety」→ 分类器请求失败。通常是暂时的;在 Bedrock 上可能是账号还调不了指定的分类器模型
查看默认规则
claude auto-mode defaults
以 JSON 打印完整的规则列表。经常被误拦的话,管理员可以用 autoMode.environment 加可信仓库/桶/服务(见 Configure auto mode)。
用 ask 规则加人工检查点
auto 模式下想在某些命令前停一下(比如 push):
{
"permissions": {
"ask": ["Bash(git push *)"]
}
}
📘 官方:ask 规则按命令字面形式匹配,所以
git -C . push这种写法不受约束。
7.7 沙箱:操作系统级的边界
它是什么
📘 官方:Bash 沙箱是操作系统强制的边界,包住 Claude 在你机器上运行的 shell 命令。你设定这些命令能碰哪些文件、能访问哪些网络域名;限制在命令运行期间由系统施加。
覆盖 Bash、PowerShell、Monitor 命令以及它们启动的进程。只覆盖 shell 命令——Claude 的文件工具、MCP 服务器、hooks 在沙箱之外运行。
平台:macOS(Seatbelt)、Linux、WSL2。原生 Windows 不支持——要在 Windows 上用沙箱,在 WSL2 里跑 Claude Code。
默认边界
| 访问 | 默认 |
|---|---|
| 写 | 工作目录、每用户临时目录、你添加的目录;受保护路径仍然禁止写 |
| 读 | 机器的大部分,包括 ~/.ssh、~/.aws/credentials 这类凭证文件 |
| 网络 | 没有直接出口。连接走本机代理逐 host 校验允许域名,允许列表初始为空 |
| 环境变量 | 从 Claude Code 继承,包括它环境里的密钥 |
⚠️ 踩坑提醒(重要):默认配置下沙箱不会保护你的 SSH 私钥和云凭证。要保护它们读
sandbox.credentials设置(见 7.7 末尾)。
开启
/sandbox
Linux / WSL2 需要 bubblewrap 和 socat。mac 直接可用。/sandbox 面板会告诉你缺什么。
或在设置里:
{ "sandbox": { "enabled": true } }
两种沙箱模式
| 模式 | 行为 |
|---|---|
| Auto-allow | 命令在沙箱内跑 → 自动批准,不弹窗;因为匹配 excludedCommands 或走了非沙箱重试而在沙箱外跑 → 走正常权限流程 |
| Regular permissions | 所有 Bash 命令都走正常权限流程,即使在沙箱内 |
Auto-allow 下仍然生效的:
- 匹配 Bash deny 规则的命令仍被拦
- 针对关键路径的
rm/rmdir仍走权限流程 - 内容型 ask 规则(如
Bash(git push *))仍强制提示 - 裸
Bashask 规则(或Bash(*))对沙箱内命令跳过,对沙箱外命令仍生效;但在 plan mode 下不跳过 - Monitor 工具命令不自动批准(虽然仍在沙箱内跑)
🔍 补充细节:auto-allow 与你的权限模式独立工作。即使你在 Manual 模式,开了 auto-allow 之后沙箱内的 Bash 命令也会自动执行。这是「安全 + 不烦人」的最佳组合之一。
非沙箱重试(逃生舱)
沙箱挡住网络连接时,结果里会写明被拒的 host,Claude 看到后可能用 dangerouslyDisableSandbox 参数重试。
谁批准这次重试:
| 模式 | 行为 |
|---|---|
bypassPermissions |
不问,直接跑 |
Manual / acceptEdits |
弹出 “Bash command (unsandboxed)” 提示 |
auto |
另一个分类器评估底层命令 |
dontAsk |
拒绝 |
| plan | 见官方 plan mode 说明 |
想强制「绝不逃逸」:
{
"sandbox": {
"allowUnsandboxedCommands": false,
"failIfUnavailable": true
}
}
这就是 /sandbox 里的 Strict sandbox mode。
保护凭证
{
"sandbox": {
"credentials": {
"files": [
{ "path": "~/.ssh", "mode": "deny" },
{ "path": "~/.aws/credentials", "mode": "deny" }
]
}
}
}
🔍 补充细节:具体字段名以 settings reference 的 sandbox 一节为准。核心思路是:默认读权限很宽,必须显式 deny 才安全。
让某些命令走沙箱外
{
"sandbox": {
"excludedCommands": ["docker *"]
}
}
7.8 工作目录
什么是工作目录
你在哪个目录启动 claude,它就是工作目录。只读工具默认只能碰这里。
加目录
claude --add-dir ../shared-lib
或写进设置:
{
"permissions": {
"additionalDirectories": ["../shared-lib"]
}
}
📘 官方:additional directories 只授予文件访问权,不授予配置。别指望在
../shared-lib/.claude/settings.json里的规则会生效。
切换会话目录
会话中途可以用 /add-dir(或对应命令)扩展。
7.9 检查点与回滚
什么时候创建
📘 官方:每次你发送一个开启新回合的提示词时,Claude Code 捕获代码状态。会话内保留最近 100 个检查点的快照。
检查点随会话一起保存,所以恢复会话后仍能 /rewind。
打开回滚菜单
/rewind- 输入框为空时按两次
Esc
⚠️ 踩坑提醒:输入框里有文字时,双击
Esc是清空输入,不是打开菜单。清掉的内容存进历史,↑可以找回。
六个选项
| 选项 | 效果 |
|---|---|
| Restore code and conversation | 代码和对话都回到那个点 |
| Restore conversation | 只回滚对话,保留当前代码 |
| Restore code | 只回滚文件,保留对话 |
| Summarize from here | 从这个点往后压缩成摘要 |
| Summarize up to here | 这个点之前的压缩成摘要,后面的消息原样保留 |
| Never mind | 什么也不做,返回列表 |
两个 restore code 选项只在选中的检查点有可回滚的文件改动时出现。
引导摘要
高亮某个 Summarize 选项后,在 add context (optional) 处直接输入指令再按 Enter:
保留所有改动过的文件列表和测试命令
按数字键则直接总结、不带指令。
局限(必须知道)
| 局限 | 说明 |
|---|---|
| Bash 命令的改动 | 只有 Claude 用文件工具改过的文件,之后被 Bash 改才能回滚。只被 Bash 改过的文件不追踪 |
| 子代理的改动 | 通常不捕获。前台 fork 的 skill 例外(context: fork + background: false) |
| 外部改动 | 你在 Claude Code 之外手动改的、其他并发会话改的,不追踪 |
| 回合中途发的消息 | 后排队进来的消息没有自己的检查点,标记为 No code restore;回滚要选「开启该回合的那条提示词」 |
| 符号链接/硬链接 | 不会恢复,会提示 Restored the code, but skipped N files。dotfile 管理器软链进来的配置、pnpm 硬链进来的包都属此类 |
| 30 天后 | 快照随会话保留期被清掉,回滚会报 No files were restored。想留久点就调大 cleanupPeriodDays |
⚠️ 踩坑提醒:
rm -rf删掉的文件、数据库迁移、git push到远端——这些都不是文件快照能救的。它们只能靠权限模式、ask 规则和你自己。
回滚不是版本控制
📘 官方:检查点是为会话级快速恢复设计的。永久的版本历史和协作,继续用 git。
7.10 安全实践清单
日常
- ☐ 在项目根目录启动,不在
~里启动 - ☐ 用 auto 模式做「方向可信」的任务,敏感操作切回 Manual
- ☐ 给
git push、rm -rf、terraform destroy加ask规则 - ☐ 把
.env、secrets/、~/.ssh加进deny读规则 - ☐ 开沙箱 + auto-allow,兼顾安全和效率
- ☐ 明确 deny 掉
~/.ssh、~/.aws/credentials的沙箱读权限
无人值守
- ☐
claude -p用--permission-mode dontAsk+ 精确的--allowedTools - ☐ 或用
--permission-mode auto - ☐ 只在容器/VM 里用
--dangerously-skip-permissions - ☐ 容器里以非 root 用户运行
关于 Claude 的承诺
📘 官方:权限规则由 Claude Code 强制执行,不是模型。你在提示词或
CLAUDE.md里写的指令会影响它「想做什么」,但不会改变 Claude Code 允许什么。
这句话很重要:别指望在 CLAUDE.md 里写「永远不要删文件」来获得安全保障。 那是建议,不是约束。要硬约束就用 deny 规则或 hook。
7.11 小结
- 权限管「能不能做」,检查点管「能不能撤」,都不能替代 git
- 六种模式:
default/acceptEdits/plan/auto/dontAsk/bypassPermissions - 规则求值顺序 deny → ask → allow,具体程度不影响顺序
Bash(ls *)不匹配lsof;Bash(ls*)匹配。结尾*前的空格是规则的一部分- 项目级设置里的
auto和bypassPermissions不生效,要写用户级 - auto 模式靠分类器,降低弹窗但不保证安全
- 沙箱默认不保护
~/.ssh和云凭证,要显式 deny - 回滚只管文件工具改过的文件;Bash 改的、子代理改的、外部的都不管
下一步 → 第 8 章《CLAUDE.md 与记忆》
- 点赞
- 收藏
- 关注作者
评论(0)