11 月 5 日,安全公司 Apiiro 发布研究;11 月 9 日,Ars Technica 的报道让更多人注意到这件事。结论先说清楚:不是点一下「不再询问」就必然泄露,但一旦带密码的命令被存进项目文件、又被提交,删掉当前文件也来不及了。
密码是怎么进去的
Claude Code 执行 Bash 命令前会弹确认框。选「Yes, and don’t ask again」,它会把这条命令写成 allow 规则,存到仓库根目录的 `.claude/settings.local.json`。
规则可能是一条命令前缀,也可能是一条完整命令。星号之后的内容不记录,但密码如果落在星号之前,就会跟着明文进文件。
Apiiro 报告里的示例很直观:确认框第二个选项会保存 `PGPASSWORD=Fk7pQ2xT9mWv4Lz psql *` 这样的整段前缀,示例里的密码是假的。
所以「点过」不等于「中招」:要同时满足命令里带明文密钥、文件又被提交或推送。如果保存下来的是 `npm run *` 这种纯模式,文件里只有模式,没有密码。
Ars Technica 的表述更直接:规则有时写成模式,但命令本身也经常被原样写入,包括夹在其中的敏感信息。Ars Technica
为什么全局忽略没挡住
Claude Code 文档写明:它第一次写这个文件时,会把文件加进全局 git excludes,而不是项目 `.gitignore`。 这层保护只跟着这台机器走,换机器、换容器、复制项目都挡不住。Claude Code 文档
一旦提交,`.gitignore` 对已跟踪文件也无效。
数字比想象中大
Apiiro 扫了公开 GitHub 上 104,185 份这个文件,1,122 份含真实密钥,去重后 1,379 条。
算上历史提交、共享配置和 agent 对话记录,总数是 2,539 条,分布在 1,994 个仓库、63 家组织。微博
更尴尬的是,Apiiro 追踪的 1,105 个含真实密钥仓库中,有 539 个出现过 Claude 联合署名、且改动了这个文件的提交。 这让「文件都是用户手动创建并提交」的说法,至少盖不住全部情况。Apiiro
官方回应了什么
Claude Code 负责人 Boris Cherny 在 Hacker News 上回应:Claude Code 过去没有做密钥清理,正在研究怎么防止。
他说 `.claude/settings.local.json` 本应留在本地,Claude 创建文件时会自动 gitignore;言下之意,泄露的多是用户手动创建并提交的。Hacker News
他建议改用 auto mode,并开启 GitHub 的 secret scanning。
关于修复,他的说法是先做几个原型,评估取舍后再发。Hacker News
这些建议都有道理,但没有回答一个关键问题:文件已经被提交、密钥已经进了历史,auto mode 和忽略规则都补不回来。
现在该做什么
顺序很重要:先轮换,再清理。密钥可能仍然有效,公开历史也可能已经被克隆或 fork。
第一步,看文件有没有被跟踪:
```
git ls-files .claude/settings.local.json
```
第二步,看历史里有没有:
```
git log --all --oneline – .claude/settings.local.json
```
第三步,找明文:
```
grep -nE ‘PASSWORD=|TOKEN=|SECRET=|API_KEY=|sshpass -p|mysql -p’ .claude/settings.local.json
```
如果提交过,把文件写进项目 `.gitignore`,再执行 `git rm --cached .claude/settings.local.json`。要清历史,得用 `git filter-repo` 之类的工具。只删当前文件不够:Apiiro 的样本里,历史提交中还压着 734 条。
别把扫描器当保险。Apiiro 的样本里,1,122 份含密文件中有 215 份连 gitleaks 和 TruffleHog 都没报。小红书
`PGPASSWORD=` 后面那种看起来像普通词的密码,尤其容易漏。
以后让命令读环境变量,别把密码写死在命令行;点「不再询问」前看一眼保存的规则,别让密码留在前缀里。
省一次确认的代价,可能是把密码写进版本历史。工具在修,但已经提交的记录不会自己消失。