跳转至

08. 权限与安全

Claude Code 的权限系统建立在一个事实之上:模型没有直接执行能力。模型只能输出工具调用,本地运行时才决定是否调用处理器。权限、安全检查、沙箱和用户确认都发生在本地,因此模型即使在文本里“承诺安全”,也不能绕过运行时规则。

权限逻辑独立于提示词,并且会把拒绝、询问、自动放行和沙箱失败都表达成模型下一步能理解的结果。

flowchart TB ToolUse[模型输出工具调用] --> Find[找到工具] Find --> Validate[输入校验] Validate --> PreHook[PreToolUse hook] PreHook --> Rules[deny / ask / allow 规则] Rules --> ToolCheck[工具自身 checkPermissions] ToolCheck --> Mode[permission mode] Mode --> Sandbox[沙箱 / 安全检查] Sandbox --> CanUse[canUseTool] CanUse -->|允许| Execute[执行处理器] CanUse -->|询问| Dialog[用户确认或自动策略] CanUse -->|拒绝| Deny[生成拒绝工具结果] Dialog -->|允许| Execute Dialog -->|拒绝| Deny Execute --> Result[工具结果] Deny --> Result

显式规则:allow / deny / ask

权限判断先看显式规则。用户设置、项目设置、本地设置、命令行、策略配置和会话内修改都可能提供允许、拒绝、询问规则。拒绝的优先级最高;询问表示即使某些模式倾向放行,也要进入确认路径;允许只是通过规则层,不代表一定执行。工具还可以有自己的权限检查,例如 Bash 会解析命令、判断子命令、路径、危险操作和沙箱条件;文件编辑工具会检查目标路径和规则匹配。

这些规则在 settings.json 里以 permissions.allow / permissions.deny / permissions.ask 数组的形式声明,规则项通常是“工具名 + 可选的参数模式”,例如只放行 git diff 相关的只读命令,或者拒绝任何触达特定敏感目录的写操作。规则来源分层叠加:企业管理策略、命令行参数、项目本地设置(不进版本库,个人本地覆盖)、项目共享设置(进版本库,团队共用)、用户级设置,层级越靠前优先级越高,这保证了团队协作场景下“组织策略不能被单个项目或个人配置绕过”。

权限模式:默认态度而非安全边界本身

权限模式决定默认态度。默认模式遇到不确定动作会询问;接受编辑模式更偏向允许编辑类操作;计划模式会限制实际修改;禁止询问模式在非交互场景下把需要询问的动作转为拒绝;绕过权限模式也不是无限制,它仍然不能越过某些工具交互要求、显式询问、安全检查和本地策略。模式改变的是“没有明确规则时怎么处理”,不是移除权限系统。

官方文档公开的权限模式命名大致是 default(不确定动作会询问)、acceptEdits(编辑类操作默认放行,便于连续改代码不被打断)、plan(只读规划模式,禁止任何写入或执行副作用,适合先看模型的修改方案再决定是否放行)、bypassPermissions(跳过大多数询问,常用于容器等已经有外层隔离的环境)。需要强调的是,即使是 bypassPermissions,也不等同于关闭权限系统——文档同时提示这类模式应当只在已有独立沙箱或容器隔离的环境下使用,本地运行时仍然保留部分强制性的安全检查。

钩子的边界:可以参与决策,不能替代规则

钩子不等于后门。工具使用前钩子可以修改输入、增加上下文或给出允许/拒绝/询问建议,但钩子的允许结果不会绕过拒绝规则、询问规则、工具安全检查和需要用户交互的限制。运行时会用规则检查重新约束钩子决策。这样插件或项目钩子可以参与流程,但不能把安全边界降级成提示词约定。

结合官方 hooks 文档看,PreToolUse 钩子的返回值可以是“允许”“拒绝”或“询问”其中之一,但这只是钩子这一层的建议;即使钩子给出“允许”,后续仍要经过规则检查、工具自身的权限检查和权限模式判断,钩子无法单方面把一个本该被拒绝的动作直接放行。这个设计和 Claude Agent SDK 里 canUseTool 回调的定位是一致的——它们都是“决策链路上的一个节点”,而不是唯一权威。

沙箱:限制执行环境,不是唯一防线

沙箱是权限系统的一部分,但不是唯一防线。Bash 等开放世界工具可以在沙箱中运行,限制文件系统写入、网络域名、设置文件访问和某些危险路径。沙箱配置会综合设置、权限规则、当前工作目录、额外工作目录和 WebFetch 域名规则。即使开启沙箱,也仍然需要判断命令是否适合沙箱、是否被排除、是否需要用户确认。被排除命令只是便利配置,不应被理解为安全边界本身。

据公开资料,Claude Code 在部分平台上支持对 Bash 工具做操作系统级别的执行隔离,限制可写入的文件系统路径和可访问的网络地址集合;具体隔离实现依赖底层操作系统能力(不同平台机制不同),本站不对具体沙箱技术选型做确定性断言。工程上可以类比 Codex 侧“沙箱 + 执行策略”的组合(详见 codex-agent-source/08-permissions-safety.md):两者都不把沙箱当成唯一防线,而是让沙箱和规则层、审批层共同收窄命令的实际可造成的影响面。

交互式与非交互式入口的不同处理

交互式和非交互式入口的处理不同。交互式终端界面可以弹出权限确认,显示工具输入、风险说明和允许/拒绝选项。非交互式 SDK 或一次性执行没有用户界面,如果策略不允许自动批准,就必须拒绝或失败。自动模式可以借助分类器判断部分请求是否安全,但分类器失败时默认保守处理,并且安全检查不可由分类器随意覆盖。

这一点在把 Claude Code 接入 CI/CD 或后台脚本时格外重要:非交互场景没有人来响应终端确认提示,如果继续用 default 模式,遇到需要询问的动作只会挂起等待,不会有人应答。这类场景通常需要显式收紧规则(用 allow 列表明确放行范围)而不是简单切到 bypassPermissions,否则相当于把风险判断完全交给模型的“自觉”。

权限拒绝是模型的输入,不是异常

权限拒绝也会回到模型。运行时不会默默吞掉失败,而是生成工具结果,说明工具没有执行或权限被拒绝。模型下一轮采样可以据此调整方案,例如改用只读命令、请求用户授权、解释无法继续,或选择不需要该工具的路径。安全系统不是循环外的异常,而是智能体推理输入的一部分。

典型结果可以按四类理解:

结果 本地动作 回到模型的内容
普通失败 工具已执行 退出码、错误输出或失败原因
权限拒绝 工具未执行 拒绝规则、所需确认或受限范围
沙箱阻止 工具被隔离层拦截 沙箱失败原因和是否可请求升级
用户拒绝 工具未执行 用户拒绝或非交互策略拒绝

模型动作面由工具结构说明决定,本地执行面由处理器决定,两者之间由权限系统隔开。模型可以建议、请求、计划,但不能直接读写文件、运行命令或调用 MCP。真正产生副作用的每一步都必须通过本地运行时的校验、规则、沙箱和确认流程。下一章会说明这些动作如何形成状态、事件和可恢复记录。