防止 Claude Code 读取密钥的隔离思路
文章指出,工作目录内的文件读取通常无需审批,.gitignore 也不能阻止 Agent 访问 .env。作者强调仅配置拒绝规则仍可能留下命令行读取路径,并以恶意扩展事件说明密钥暴露风险。
AI 深度解读
把 .env 加进 .gitignore,只能阻止它进入 Git 提交,拦不住 AI 编程工具从磁盘读取。作者提醒:即使给 Claude Code 配了禁止读取规则,Agent 仍可能通过递归搜索、Node 脚本等方式碰到密钥。
这篇最有用的地方,是把工具权限、命令沙箱和编辑器自身的安全边界分开了。它给出了一套防护配置,也指出了配置管不到的地方。对经常让 Agent 排查数据库连接、环境变量和部署问题的程序员,这比单纯提醒「别泄露密钥」更有操作价值。
为什么禁止 Read,还挡不住 Node 读取密钥
按原文引用的权限文档,Claude Code 在工作目录内使用文件读取、Grep 等只读工具,不需要审批。项目里的 .env 因而可能直接进入模型上下文,随后出现在调试输出中。
作者先在用户级 ~/.claude/settings.json 设置拒绝规则,让配置覆盖所有项目:
{
"permissions": {
"deny": [
"Read(.env)",
"Read(.env.*)",
"Read(**/secrets/**)",
"Read(~/.ssh/**)",
"Read(~/.aws/**)"
]
}
}
要看清前两行的路径语义:据原文,Read(.env) 按 gitignore 语义匹配,能覆盖 monorepo 中的 apps/api/.env;Read(./.env) 只针对当前目录的文件。用户配置中的 Read(/secrets/**) 则会锚定到 ~/.claude,无法保护项目里的同名目录。
这些规则覆盖 Read,并对 Grep、Glob、@file 引用及 IDE 共享的打开文件上下文做尽力检查;同路径的 Edit、Write 也受约束,其中 Write 需要 v2.1.228+。Claude Code 能识别的文件命令,如 cat、head、tail、sed、tee,也在检查范围内。输入重定向 < .env 从 v2.1.257 开始检查,tee 的目标路径从 v2.1.269 开始检查。
但命令可以不直接写出文件名。grep -r DATABASE_URL . 会遍历目录;node -e "require('dotenv').config(); console.log(process.env)" 则由 Node 自己打开文件。这两类间接访问,原文明确列为 deny 规则的缺口。
禁止某个工具读取文件,不等于禁止它启动的进程读取文件。
另外,原文称 .claudeignore 已不起作用,条目应迁入 deny 规则。配置后运行 /status,检查 Setting sources 是否包含 User settings,避免文件写了却没有加载。
真正补上缺口,要让操作系统拒绝访问
作者的第二层防护是沙箱(sandbox)。macOS 使用 Seatbelt,Linux 和 WSL2 使用 bubblewrap;按原文引用的文档,Read、Edit 的拒绝规则会合并进最终沙箱配置。此时,递归搜索和 Node 子进程的文件访问也受到操作系统约束。
下面这段配置用于启用沙箱,并阻止执行过程退回无沙箱状态:
{
"sandbox": {
"enabled": true,
"failIfUnavailable": true,
"allowUnsandboxedCommands": false,
"filesystem": {
"denyRead": ["~/.ssh", "~/.aws"]
}
}
}
重点是 failIfUnavailable 和 allowUnsandboxedCommands 两行。前者要求沙箱不可用时退出,因为默认行为可能继续无沙箱执行;后者关闭失败后通过 dangerouslyDisableSandbox 重试的通道。在 bypassPermissions 模式下,这种重试原本可能不再询问用户。
沙箱也需要明确划定范围。原文说,它默认允许读取机器上的大量文件,包括凭据,因此示例额外隐藏 ~/.ssh 和 ~/.aws。原生 Windows 的命令没有这层沙箱,作者建议使用 WSL2。
v2.1.257 及之后,还可以在 permissions 下设置 "blockReadsOutsideWorkingDirectories": true,让 Read、Grep、Glob、LSP 在包括 bypassPermissions 在内的模式中拒绝项目外路径。它控制的是这些工具的访问范围,不能代替命令沙箱。
作者另加了 PreToolUse hook:保存为 .claude/hooks/block-secrets.sh,执行 chmod +x,再在项目 .claude/settings.json 中以 matcher: "Bash"、type: "command" 注册,命令路径为 ${CLAUDE_PROJECT_DIR}/.claude/hooks/block-secrets.sh,args 为 []。
脚本通过 jq -r '.tool_input.command // empty' 提取命令,用正则检查 .env、secrets/、.ssh/、id_rsa、id_ed25519 和 .aws/credentials 等字符串。命中后向 stderr 写原因,并以退出码 2 阻止调用。
这层检查会误拦 process.env.FOO、.env.example,却漏掉 .envrc 和 grep -r pattern .。所以作者把它当作清晰的报错提示,真正的文件隔离交给沙箱。
换成 Cursor 或 Codex,也要检查命令的权限
据原文,Cursor 默认尊重 .gitignore,内置忽略列表还包含 .env*,也支持 .cursorignore。但 Agent 的终端和 MCP server 工具不受 .cursorignore 阻挡,仍存在间接读取的入口。作者没有给出对应的 Cursor 沙箱配置。
Codex 的方案是文件系统权限配置:在 ~/.codex/config.toml 中设置 default_permissions = "project-edit",让 [permissions.project-edit] 通过 extends = ":workspace" 继承工作区权限,再在 [permissions.project-edit.filesystem.":workspace_roots"] 下配置 "**/*.env" = "deny"。
这里有两个前提。任何生效配置层出现 sandbox_mode,或命令行传入 --sandbox,都会按原文所述切回旧沙箱设置,导致该权限配置不适用。Linux、WSL 和部分 Windows 模式中的 glob 匹配,在每个沙箱进程启动时确定;长期运行的开发服务器,可能无法把后来创建的 .env 纳入拒绝范围。
还要区分文件和环境变量。密钥如果已经由父进程传入,限制文件读取并不能收回它。原文建议设置 shell_environment_policy.ignore_default_excludes = false,让 Codex 从子进程环境中排除名称包含 KEY、SECRET、TOKEN 的变量。
我的判断
我认同作者把防护放到操作系统层的方向。提示词要求 Agent「别读密钥」,以及 hook 对命令文本做匹配,都不能覆盖进程实际访问文件的所有路径。对会运行 Node 脚本、递归搜索和构建命令的工作流,这个区别很实际。
不过,原文提供的是作者的配置经验和文档解释,没有给出完整复现记录、绕过测试矩阵,也没有与其他方案的对照数据。「十分钟完成」是作者的估计,不能据此判断一般团队的迁移成本。
文章引用的供应链事件还揭示了另一条边界:据原文,Phorion 在 10 月 30 日披露 Cursor Marketplace 中带后门的「Claude Code for VS Code」构建,涉及 2.1.312、2.1.313。恶意构建添加 SSH 公钥、建立反向隧道,并回传最多 40 个匹配 KEY|TOKEN|SECRET|PASS|AWS|GCP 的环境变量名称。原文没有说这里回传的是变量值,不能自行扩大描述。
这种扩展运行在编辑器内部,拥有用户权限,根本不经过 Claude Code 的工具审批。Agent 的沙箱无法替编辑器扩展建立安全边界。
因此,独立系统账号或容器,以及从 vault、op run 在运行时注入密钥,有各自的价值。但运行时注入也不能让密钥对接收它的进程隐身,仍要控制哪些进程获得哪些凭据。
能拿来做什么
- 给日常 Claude Code 工作流同时配置 deny 和沙箱,并检查
/status中的加载来源。用无价值的测试文件分别验证直接读取、递归搜索和 Node 间接读取,确认限制真正生效。 - 排查前端或 Node 环境问题时,让
.env尽量只保存可丢弃的本地凭据。使用 direnv 的项目要另外覆盖.envrc;照搬原文 hook 会漏掉它。 - 使用 Codex 时,检查权限配置是否被
sandbox_mode或--sandbox覆盖;开发服务器启动后新建敏感文件,也要重新评估已有进程的保护范围。 - 如果安装过上述 2.1.312、2.1.313 构建,按原文线索运行
grep -n svc-telemetry ~/.ssh/authorized_keys,并在 macOS 检查~/Library/LaunchAgents/com.apple.sysmond.plist。SSH 注释容易修改,还应运行ssh-keygen -lf ~/.ssh/authorized_keys,核对指纹SHA256:0Z6R+DF64Do2nwQic8a7kkfDLNNk4IiK+2U2Rci4htk;单个线索未命中不能证明没有问题。