Claude Code 凭据泄露:点一次「不要再问」,1,122 份凭据进了公开仓库
11 月 5 日,安全公司 Apiiro 发了一份关于 Claude Code 凭据泄露的研究:GitHub 上 104,185 份 .claude/settings.local.json 里,1,122 份含有真实凭据——数据库密码、云厂商 API key、SSH 服务器 root 密码,甚至一家数据库厂商自己的管理级云密钥。把历史提交、团队共享的 settings.json 和 agent 会话记录都算进去,总量是 2,539 个真实凭据,散落在 1,994 个公开仓库。
这条线索没有停在研究报告上。帖子在 Hacker News 上拿到 500 分、358 条评论,Anthropic 的 Claude Code 负责人 Boris Cherny 在 11 月 7 日进场回应,周末连发了十条。官方第一次公开承认:Claude Code 历史上从来没做过凭据清洗(secret scrubbing)。
说明一下与前一篇的关系:站内 1148 那篇讲的是 Claude Code 把账号邮箱写进 User-Agent 发给第三方网站,是外发路径的问题。这一篇完全不同——是留存在本地、再被自己人提交进仓库的问题,而且这个文件本来就是 Claude Code 自己写的。
一句话结论
- 这不是漏洞,是设计。你点「是,并且不要再问」,Claude Code 就把整条命令原文存成一条 allow 规则写进项目目录里的
.claude/settings.local.json。命令里带密码,规则里就带密码。 - 防护走错了通道。Claude Code 确实尝试把文件挡在提交之外,但它加的是你的全局 gitignore,不是仓库自己的
.gitignore。换个机器、换个容器、复制一个文件夹、或者文件已经被跟踪,这层防护就失效。 - 通用扫描器救不了你。gitleaks 和 TruffleHog 各扫一遍 104,185 份文件,有 215 份两边都没报。漏掉的恰恰是
PGPASSWORD=、sshpass -p后面那种样子普通的密码。 - 官方的回应把责任外推。Cherny 的建议是「大家都该用 auto 模式」「你们都该开 GitHub 的 secret scanning」。报告作者和评论区指出:auto 模式遇到敏感命令同样会问,而默认选项是「始终允许」,同一个坑还在。
- 最实用的一条结论:别把密码打进命令里,用
$VAR。规则记录的是$DB_URL而不是那个密码,这个坑就从结构上消失了。
1)一条命令,怎么变成一次公开泄露
机制很简单,而且每一步都很合理。
开发机上本来就有凭据:配置文件里的数据库连接串、本地装的云厂商 key、.env 里的 API token。你让 Claude Code 去看「staging 为什么登录失败」或者「跑一下这个 migration」,它自然会提出一条带凭据的命令——不带就跑不通。Claude Code 会先问你要不要执行,一个会话里要问很多次。问到第十次,基本没人再逐字读命令行,而一个密码在一条长命令里只占一个词的位置。
你选了「是,并且不要再问」。Claude Code 于是做两件事:执行这条命令,并把这条命令本身作为一条 allow 规则,追加到项目文件夹里的 .claude/settings.local.json。
Apiiro 特别写明:这是 Claude Code 程序本身的行为,不是模型的行为。他们用 Opus 5.5 和 Fable 5.1 都复现了。也就是说,换个更强的模型不会好一点。

问题的关键在最后一环。settings.local.json 本该只留在开发机上,Claude Code 也确实想把它留住——它在创建这个文件时,会把一条匹配 .claude/settings.local.json 的规则写进你的全局 git 排除文件(core.excludesFile,或 ~/.config/git/ignore)。这覆盖了常见情况,但漏掉四种:文件夹之后才变成仓库、换一台机器克隆、文件已经被提交过、以及有人手工建了这个文件。
而且仓库自己的 .gitignore 从头到尾没有被改过。所以「保护」这件事从来没有跟着项目一起走——换台机器 clone 下来,它就是一堆普通文件里的一个普通文件。
更麻烦的是 Claude 会自己提交它。在 Apiiro 追踪到有真实凭据的 1,105 个仓库里,539 个存在一个由 Claude 共同署名(co-authored)的提交改动了这个文件,署名覆盖从 Sonnet 4.5 到 Opus 5.5 的各代模型。Claude Code 的提交指引明确叫模型不要提交 .env 和 credentials.json——唯独没把自己写的这个设置文件写进名单。
2)Claude Code 凭据泄露的规模有多难看
先把统计口径说清,因为这里的数字很容易被误读。
| 统计项 | 数量 |
|---|---|
| 公开 GitHub 上的 settings.local.json 副本 | 104,185 份,分布在 103,617 个仓库 |
| 其中含真实凭据的 | 1,122 份(1.1%) |
| 这 1,122 份里的不同凭据 | 1,379 个(636 个厂商格式 key + 743 个密码/token) |
| 历史提交、共享 settings.json、会话记录合计 | 2,539 个凭据,1,994 个仓库 |
| 已删除、但仍在历史里的凭据 | 734 个,涉及 509 个仓库 |
| 被 fork 带走的 | 74 个仓库的 211 个 fork |
「真实凭据」的口径需要说明:匹配已知厂商 key 格式,或者一个密码/token 紧邻一个公网主机。Apiiro 明确表示没有使用或测试任何一个凭据,也没有联系任何主机——所以这不是「确认有效」的统计,是「看起来能用的」统计。起作用的过滤规则去掉了占位符、常见默认密码、本地和私有主机、以及设计上就公开的 key(比如 Supabase anon key、Firebase web config),最后剩下的 2,774 个值逐一交给独立 LLM 评审复核。
另外两个数字值得单独看。
一是「文件越大越危险」。超过 8 KB 的 allowlist 里,每十份就有一份含真实凭据;500 字节以下的,不到五百分之一。这条曲线基本就是「你点了多少次『不要再问』」的曲线——用得越久,攒得越多。
二是「躺了很久没人发现」。在有真实凭据的仓库里,这个文件首次被提交的时间,中位数是 2026-11-02 之前的 217 天;有 118 个仓库超过一年。这不是上周刚出事,是一个长期没人看的角落。
顺带说一句:Apiiro 对比了 16 款编程 agent 的文档,只有 Claude Code 被确认会在默认行为下把整条被批准的命令写进项目文件。这句话的分量比「1,122」这个数字更大,因为它指向的是设计选择。
3)被曝的是谁
63 个组织持有真实凭据。报告里点了类型,但没点名字(他们按承诺在 11 月 2 日和 3 日逐个通知了这些组织)。
| 类型 | 曝出来的东西 |
|---|---|
| 一家数据库厂商 | 自己的云 API key,带 manage 权限、无过期声明,外加集群主机 |
| 一家 B2B 创业公司 | 生产环境 Azure Postgres 密码 |
| 一家软件代理公司 | Square 生产环境 access token |
| 一个千星以上开源项目 | OpenAI 格式 API key |
| 一家 IT 服务商(医疗记录应用) | 该应用云 Postgres 数据库的 owner 密码 |
| 一家支付创业公司 | postgres 角色的 Postgres 密码 |
| 一个大学仓库 | 对一台公网服务器的 root SSH 永久授权 |
凭据类型分布也值得看:超过三分之一直接开数据库——379 个数据库密码加 122 个 Supabase service-role key;另有 103 个 Supabase 账号 token,能控制该账号下的每一个项目。剩下的大头是第三方服务和模型 API key,再加 43 个 SSH 登录、42 个 GitHub / GitLab / npm token、23 个 AWS key、6 个私钥。
注意这里有个很容易被忽略的点:没有一个是故意公开的。key 在命令里,命令被批准了,文件就跟着其他代码一起被提交了。整个过程里没有一步需要人做错决定。
4)为什么通用扫描器漏掉了 215 份
这是我认为整份报告里最该被记住的一节,因为它不只关于 Claude Code。
Apiiro 把 gitleaks 和 TruffleHog 对全部 104,185 份文件跑了一遍。在 1,122 份含真实凭据的文件里,gitleaks 报了 564 份,TruffleHog 报了 643 份,215 份两边都没报。
缺口在于「命令里的密码」。扫描器找的是 key 的形状:厂商前缀、长随机串。而 PGPASSWORD=、sshpass -p、mysql -p 后面跟的密码,或者 curl 里的 bearer header,在一条普通命令行里就是一个普通单词,两个工具基本都放过。
这个模式的影响面远大于这一个文件:只要 agent 记录了自己跑过什么,同样的密码就会出现在会话记录、记忆文件、交接文档里,而一个按 key 格式调优的扫描器在所有这些都是失效的。换句话说,你现在配的那套扫描,大概率覆盖不到这类泄露。
5)不止一个文件
allowlist 只是最清楚的一个例子。agent 还会把凭据写进别的、开发者会提交的文件里。
| 文件类型 | 检查了多少 | 含真实凭据的 |
|---|---|---|
团队共享的 .claude/settings.json | 76,011 份 | 298 份 |
| agent 会话记录(.specstory、aider、Cursor 导出) | 2,523 份 | 87 个仓库里的 120 份 |
| agent 记忆文件(MEMORY.md 一类) | 1,200 份 | 9 个仓库 |
| 交接与部署笔记 | 2,095 份 | 33 份 |
| VS Code Copilot 自动批准设置 | 173 份 | 9 个仓库 |
还有几个容易漏掉的出口:31,053 个 Hugging Face Space 里有 126 个持有这个文件,其中一个的 allow 规则里有一个数据库服务 key 和一个用户密码;GitLab.com 上抽查的 69 份公开副本里 2 份含真实凭据,其中一个是 GitLab access token。
即使文件一辈子没上 GitHub,开发机上也一样不安全。它就是项目文件夹里的明文 JSON。这个风险不是理论上的:2025 年 9 月的 Shai-Hulud npm 蠕虫会下载 TruffleHog 扫受害者磁盘——而 TruffleHog 恰好能报出这 1,122 份里的 643 份;再早三周,恶意的 Nx 包直接要求开发者本机的 Claude Code 和 Gemini CLI 去搜磁盘找凭据。文件也可能不经 git 就走:打进 npm 包、被 Docker 镜像里的 copy 命令带进去、装进 zip、落到某个同步盘。
6)11 月 7 日之后:官方回应和三个争议点
Apiiro 在发布前就把研究给了 Anthropic。文章上线后,Boris Cherny 于 11 月 7 日在 HN 上回应,周末发了十条评论。他的三条主张值得逐条看。
主张一:这个文件本该只留在本地,是我们自动 gitignore 的,所以这些是手工创建并提交的。
官方文档确实这么写:Claude Code 第一次在没忽略它的 git 仓库里创建该文件时,会把一条匹配 .claude/settings.local.json 的规则加进全局 git 排除文件。但「全局」正是问题所在——它不随仓库走。评论区有人直接追问:那为什么在 1,105 个仓库里有 539 个是 Claude 自己共同署名改动的?报告作者也指出文档里另一处写着:文件若已被 git 跟踪,其 allow 规则就要走 workspace trust 那一步,而用户不会被告知,下一次批准照样追加进那个被跟踪的文件。
主张二:权限规则是老一套了,大家都该用 auto 模式,更安全。
这是最有争议的一条。auto 模式从 2026 年 8 月 14 日起成为 Claude Code 在 Pro / Max / Team 上的默认,Anthropic 有一篇专门的文章讲他们怎么测的:内部红队、第三方红队、提示注入评测、1,053 名付费测试者的对照实验,结论是「每一项指标上都持平或更好」。这套论证本身站得住——人的注意力有限,是真实存在的攻击面。
但报告的评论区指出了同一个洞:auto 模式遇到敏感命令时同样会问,而那个提示的默认选项是「始终允许」,照样会把命令存下来。Cherny 的回应是「说得对,我们在想怎么改进」。所以换成 auto 模式并不自动解决这个问题,它只是把提问频率换成了另一种。
反方的核心诉求也很明确,一位评论者的说法很典型:我要的是确定性的边界,不是概率性的安全。如果 agent 能决定做某件事,那就没有任何东西阻止这件事被意外做出来。另一条高赞批评直接把「大家都该用 auto」读成对开发者的冒犯——因为人真正想要的是一条能改写的规则、一个能编辑的批准框,而不是只有「同意一次 / 永远同意 / 拒绝」三个按钮。
主张三:不管用什么 agent,都该开 secret scanning。
这条没错,但按第 4 节的结论,力度不够:你开的扫描器有将近两成的概率根本不报这类泄露。Cherny 自己也承认 Claude Code 历史上「没做凭据清洗」,理由是「有很多好工具做这件事」——而报告恰好证明了那些好工具在这里会漏。
他还提到团队在做的方向:让 auto 模式能抓到这种情况、内置基础凭据扫描,先做几个原型看权衡再上线。同时他两次提到 Anthropic 开源的 agent 无关沙箱 sandbox-runtime(7,029 星),说还会有更多改进。截至 11 月 6 日的最新版是 2.1.319,公开 changelog 里还看不到针对「allow 规则含凭据」的修复项。
还有一个细节不太好看:Claude Code 用户从 2025 年 8 月就在 GitHub 上报这个机制(#5544、#11982、#13106、#91270)。我逐个查了状态——四个全都关了,其中三个是被机器人以「不活跃」或「重复」自动关的,另一个(#91270,VS Code 扩展把永久授权写进被跟踪的 settings.json 而不是 settings.local.json)在 2026 年 11 月 5 日被以「不活跃太久」关闭,正好是报告上线那天。报告的说法是这些 issue 都没得到 Anthropic 的回复。
7)自查和清理:按这个顺序做
先把顺序说对,因为顺序错了会白干。先轮换,再清理——清掉文件不会清掉历史、fork 和 clone。

个人开发者
- 查公开性:在你的 GitHub 组织里搜
.claude/settings.local.json和.claude/settings.json。每一个命中都是现在任何人都能读的文件。 - 查历史:就算文件今天已经删了,也去翻每个仓库的 git 历史。历史里有,每个 fork 和 clone 里就都有。
- 如果被跟踪了:
git rm --cached .claude/settings.local.json,然后把它加进仓库自己的.gitignore。 - 轮换:这个文件历史上持有过的每一个凭据都要换。
- 就算从没提交过也要清理:打开文件,删掉每一条含凭据的规则。
- 改习惯:别把密码打进命令,用 secrets manager 里的
$VAR。这样规则记下来的是$DB_URL。 - 改批准方式:批模式而不是批原文字面,比如
Bash(npm run:*)。这也是「别再问」的正确用法。
团队和安全负责人
- 把
.claude/、.cursor/、.gemini/、.specstory/、agent 记忆文件当成.env同级别的敏感目录——笔记本上和仓库里都算。 - 把它们加进 pre-commit 和 CI 的凭据扫描,包括历史;并且确认扫描器能抓
PGPASSWORD=、sshpass -p、mysql -p、bearer header 这类「命令里的密码」,而不只是厂商 key 格式。 - 把
.claude/settings.local.json加进组织的.gitignore模板。 - 分诊优先级:先处理「凭据 + 公网主机」的组合,那些是真能直接用的。
小结
把这件事拆干净,其实只有一句话:你为了少被打断而攒下的那份「信任清单」,同时是一份明文凭据清单。它长在项目目录里,不进仓库的 gitignore,会被 agent 自己提交,躺了平均 217 天没人看,而且现有的扫描器有近两成看不出来。
Apiiro 这份报告的真正价值,是把它从「用户手滑」重新定义成「设计把两件事绑在了一起」——批准一条命令,和永久记下一个秘密,用的是同一次点击。设计层面的解法也清楚:规则记模式不记原文,密码走变量不走命令行。
对日常用 Claude Code、Codex、Cursor 的人来说,最该带走的三条是:先去自己的公开仓库里搜一遍 .claude/;扫描器要能抓命令里的密码;批准的时候看清楚那条命令里有没有一个叫密码的单词。前两条五分钟能做完,第三条每次都要做。
想在自己的机器上把几个顶级模型并排跑、对比它们的实际行为?可以看看俱乐部的 Claude Max / ChatGPT Pro 拼车方案。
参考来源
- Apiiro:Coding Agents Are Leaking Your Secrets to GitHub(2026-11-05,11-08 更新含官方回应)
- Hacker News 讨论(500 分,358 条评论,含 Claude Code 负责人 Boris Cherny 的十条回应)
- Claude Code 官方文档:Settings(settings.local.json 与全局 git 排除文件的说明)
- Anthropic:Auto mode is now the default in Claude Code(2026-08-07,10-22 更新)
- GitHub issue #5544:approved bash commands containing credentials are stored as is(2025-08-11)
- Anthropic sandbox-runtime(开源 agent 无关沙箱,7,029 星)


