AI 编码代理把密钥提交了:Claude Code 权限机制的隐患与补救
"不再询问"很爽,直到你的密钥被推上 GitHub。
一、先还原事故:密钥是怎么"跑出去"的
据安全公司 Apiiro 于 2026 年 11 月 5 日发布的研究(外媒 11 月 7 日起陆续报道),Claude Code 这类 AI 编码代理的"不再询问 / 自动批准"权限机制,会把带明文密钥的命令留存进项目配置文件,随代码一起被提交,最终导致1,994 个公开仓库里躺着 2,539 条密钥——其中 1,122 份正是 .claude/settings.local.json 这个配置文件。
这不是某个人的操作失误,而是一条被"自动化"放大的泄漏链路:
① 你在命令里用了明文密钥(如 curl -H "Authorization: Bearer sk-...")
↓
② 弹窗问你"是否允许执行",你选了"Yes, and don't ask again"
↓
③ Claude Code 把这条含明文密钥的整条命令,写进
.claude/settings.local.json 的 allow 规则(本意只是"下次别再问了")
↓
④ 这个本地配置文件被 git add、commit、push 到公开仓库
↓
⑤ 密钥随文件公开 → 泄漏
核心问题只有一个:本该由人拦下的高危动作,被"自动批准"直接放行了;而且这次放行的痕迹,还被持久化进了要提交的文件。
二、3个把风险放大的因素
| 放大器 | 表现 | 后果 |
|---|---|---|
| 自动批准(不再询问) | Agent 执行外部命令无需确认 | 高危操作无人把关 |
| 密钥进命令/文件 | 拼接进 shell 命令、脚本落盘 | 明文被写入文件或历史 |
| 批量默认提交 | 一次提交多个文件,人没细看 | 泄漏文件直接进仓库 |
3个叠加,就是"你根本没看见,它已经提交了"。
三、4道补救:从"事后救火"到"事前拦截"
补救 1:密钥只走环境变量,禁止进命令行明文
# 危险:密钥直接出现在命令里,可能被命令历史/日志记录
curl -H "Authorization: Bearer sk_live_abc123" https://api.example.com
# 安全:通过环境变量注入,命令里不出现明文
curl -H "Authorization: Bearer $API_TOKEN" https://api.example.com
配套:
.claude/settings.local.json以及.claude/下的本地配置必须进.gitignore,绝不提交;团队共用的权限配置放settings.json,且不含任何密钥;.env文件放本地密钥,必须在.gitignore里;- 给 Agent 的指令里明确写:禁止把密钥、Token 写入代码或命令行;
- 生产环境用密钥管理服务/配置中心,代码里只有引用。
补救 2:密钥扫描进 pre-commit + CI(双保险)
靠人记不住,靠门才靠得住。推荐 gitleaks:
# 本地 pre-commit 扫描暂存区,发现疑似密钥直接拦截
# 注意:v8.19 起 protect / detect 已弃用,改用 git / dir / stdin 子命令
gitleaks git --staged --verbose
# CI 里全量扫描,作为合并门槛
gitleaks dir . --verbose # 扫当前目录内容
gitleaks git . --verbose # 扫 Git 历史提交
接入 pre-commit(.pre-commit-config.yaml):
repos:
- repo: https://github.com/gitleaks/gitleaks
rev: v8.28.0 # 以官方最新 release 为准
hooks:
- id: gitleaks
这样在提交那一刻就把密钥拦下,而不是等它上了 GitHub 才发现。
补救 3:权限最小化——高危操作必须人工确认
- Agent 可以自动做读代码、改代码、跑测试这类低风险动作;
- 但 git commit / git push / 部署 / 删除 这类不可逆或外发操作,必须人工确认;
- 别为了省几秒钟,把关卡全关了。
补救 4:已泄漏,立刻轮换 + 清历史
一旦确认泄漏,删文件没用——Git 历史里还在。
# 1) 第一时间去平台吊销 / 轮换密钥(最优先)
# 2) 用 git filter-repo 清理历史中的敏感文件
git filter-repo --path path/to/secret.env --invert-paths
# 3) 强制推送并通知协作者重新拉取
git push --force-with-lease
顺序千万别错:先吊销,再清历史。密钥只要还有效,清历史也白搭。
历史重写后还有两件事:通知所有协作者重新 clone(旧的本地历史会把它带回来),以及处理 GitHub 上已存在的 fork / 缓存副本、已合并的 PR 引用——上游干净了,别人手里的副本未必。
补救之外:先自查有没有中招
- 在自己的公开仓库里搜
.claude/settings.local.json,或直接搜Bearer、sk-这类特征串; - 本地对全量历史扫一遍:
gitleaks git .; - 打开
.claude/settings.local.json,看 allow 规则里有没有你当初点过"不再询问"的外发命令。
四、AI 编码代理安全使用清单(可直接照做)
- 默认关闭"自动批准高危操作"——提交/推送/部署必须人工确认
- 密钥只走环境变量,
.env与.claude/settings.local.json一起进.gitignore - pre-commit + CI 双保险扫 secret(gitleaks / trufflehog)
- 提交前必看 diff——AI 批量提交尤其要逐文件过一眼
- 给 Agent 写安全边界(类似
AGENTS.md):禁止输出密钥明文、禁止自动推送 - 泄漏即轮换,再用
git filter-repo清历史 - 定期审计——查一遍公开仓库里有没有自己的密钥
五、写在最后
AI 编码代理的便利和风险是一体两面:它越"自动",越可能在你看不见的地方替你做了不该做的事。
"不再询问"不该等于"不再把关"。 权限可以交给 AI,但安全这条线,永远得由人来兜底——用规则、用门控、用扫描,把它拦住。
省下的那几次确认弹窗,换来的是一个上 GitHub 的密钥,不值。
参考来源
- Apiiro 研究(2026-11-05):Claude Code "Yes, and don't ask again" 将含密钥命令持久化至
.claude/settings.local.jsonallow 规则,共发现 2,539 条密钥、1,994 个公开仓库 - The Hacker News《Claude Code's "Don't Ask Again" File Exposes Passwords and Keys in 1,122 Public Copies on GitHub》(2026-11-07):thehackernews.com/2026/11/cla…
- Cyber Security News(2026-11-07):cybersecuritynews.com/claude-code…
- gitleaks 官方文档(
git/dir子命令,v8.19 起protect/detect弃用):github.com/gitleaks/gi…