2026年11月8日星期日
AI 安全

Claude Code「不再询问」功能致使密钥泄露至 GitHub

Apiiro发现1,122份公开的Claude Code设置文件含有1,379个密钥,「不再询问」审批流程将其原样保存。

插图:抓娃娃机从文件夹形玻璃箱中夺取钥匙,放入公共空间。
一次图方便的点击,悄然把凭据从私密提示中搬到了公共场所。

超过一千个公开的GitHub仓库中存有明文密码、AWS密钥和数据库凭据——原因不是有人把它们硬编码进源代码,而是开发者在Anthropic的Claude Code中点击了「Yes, and don't ask again」(即「是,且不再询问」)。这款AI编程助手会把已批准的命令原样保存到项目设置文件中,密钥也一并存入。安全公司Apiiro昨日表示,他们在1,122份公开的该文件副本中发现了至少一个密钥,相关情况见The Hacker News的报道。

影响范围并非理论上的。涉事文件分属63家机构,多为软件公司。Apiiro的研究报告披露了一些「战利品」,包括某数据库供应商自身的管理员级云密钥,以及一个病历数据库的所有者密码。研究人员本周通知的一家公司确认其密钥仍然有效。关键在于:删除文件无济于事,因为旧版本仍保存在git历史和211个fork中。轮换密钥是唯一有效的补救措施,而责任归属于仓库的所有者——无论是哪位员工、哪个智能体提交了该文件。

基础机制:一份存储完整命令字符串的白名单

按步骤梳理这条链路吧,因为这个漏洞很平淡,正因如此才得以大规模出现。默认情况下,Claude Code在运行大多数shell命令前会请求许可。如果你选择「Yes, and don't ask again」,根据Anthropic的权限文档,它会在项目内的.claude/settings.local.json中写入一条允许规则。之后匹配的命令会在后续会话中无人值守地自动运行。这正是功能按设计运作的结果。

问题出在保存的内容上。该规则涵盖的是提示中提供的任何内容——有时是类似npm test *的模式,有时是一条精确命令。当是精确命令时,你在其中输入的任何密码或令牌都会以明文持久化保存。Apiiro用最新模型Opus 5.5和Fable 5.1复现了这一行为。Hacker News讨论串中一位工程师描述了他在实际中遇到的同样情况:只批准了一次curl -H "Authorization: Bearer sk-...",令牌就一直留在设置文件里,直到有人碰巧重新读到它。

{
"permissions": {
"allow": [
"Bash(PGPASSWORD=hunter2 psql -h db.example.com -U admin)"
]
}
}

Claude Code确实会尝试让该文件远离git:首次在某个仓库中写入该文件时,它会将其加入用户的全局git排除列表,这一点在其设置文档中有说明。但这只是针对单个用户、单台机器的保护。它对已经提交的文件毫无作用,而且Claude Code从不会把该文件加入项目自身的.gitignore——因此保护措施无法随仓库一起流转。更糟的是,在Apiiro追踪的1,105个仓库中,有539个仓库里,署名Claude为共同作者的提交修改了该文件;研究人员认为这表明智能体经常在提交自己包含密钥的配置。一位在Hacker News上自称是Claude Code团队成员的评论者认为,该文件本就应保留在本地,团队正在研究防范措施;这条论坛评论尚未经证实,但它描述的机制与文档相符。

一个便利开关,2,539个密钥

Apiiro对公开GitHub的扫描共发现104,185份settings.local.json副本,分布于103,617个仓库中。约1.1%的文件——即1,122个——含有至少一个密钥,去重后共计1,379个。当某个值匹配已知的密钥格式,或紧邻某个公开服务器地址时,即被计为密钥,并需经AI审核员确认。超过三分之一的文件可直接连接数据库:其中有379个数据库密码,另有43组公开服务器的SSH登录凭据、42个GitHub、GitLab和npm令牌,以及23个AWS密钥。

时效性数据才是最令人担忧的部分。截至周一,所有含密钥的文件仍位于其仓库的默认分支上,其中半数仓库中的文件已存在至少217天。git历史中另有734个密钥分布于509个仓库,它们已不再出现在任何当前版本中——通常是因为有人删除了文件并认为问题已解决。复刻进一步扩大了泄露范围:74个仓库的211个fork与其上游仓库含有相同密钥。再加上团队有意提交的共享文件.claude/settings.json(76,011份副本中有298份含密钥,多数位于允许规则内),以及旧版本和保存下来的智能体聊天日志,Apiiro统计的总数达到2,539个密钥,涉及1,994个仓库。在研究人员比较过文档的16款编程智能体中,Claude Code是唯一被证实默认会将完整的已批准命令保存到项目文件中的产品。

有两件事仍不明确,而且都很重要。Apiiro没有测试这些密钥是否仍然有效——唯一确认仍有效的密钥来自被通知公司的自我确认。此外,研究人员也没有说明是否有人滥用了这些暴露的凭据。请将这两点视为待解答的问题,而非可以放心的理由。

插图:无休止分叉的文件夹走廊中,几个文件夹向外漏出光线。
git历史和fork会让副本在原始文件被删除后依然存活很久。

为什么gitleaks和TruffleHog都漏掉了它

如果你以为CI中的密钥扫描已经覆盖了这一问题,那很可能并非如此。Apiiro在每个文件上运行了gitleaks和TruffleHog,并关闭了它们的验证步骤。在1,122个含密钥的文件中,gitleaks标记了564个,TruffleHog标记了643个,两者都未能识别215个。漏检是结构性的:扫描器搜寻的是已知的密钥格式,而跟在PGPASSWORD=、sshpass -p或mysql -p后面的密码,在普通的命令行中看起来就像是一个普通单词。这与我们在相关工具报道中见过的检测盲区如出一辙,从智能体被一个误入的文件操控,到Cursor应用市场上被植入木马的Claude Code扩展:智能体的工作目录是一条没有人在监控的信任边界。

即使从未触及GitHub的副本也同样构成风险。该文件是项目文件夹中的普通JSON,这意味着它会随npm包、Docker镜像、zip压缩包和同步文件夹一起流转。而恶意软件早已盯上了这条路:2025年9月,Shai-Hulud蠕虫在受感染的机器上运行了TruffleHog扫描器,并将其遇到的文件中超过一半标记为密钥。笔记本电脑上一个未提交的settings.local.json,就是等待下一个带有抓取规则的信息窃取木马的战利品。

2025年8月提交,被机器人关闭

对于一直关注问题追踪器的人来说,这并不意外。2025年8月11日提交的一份缺陷报告描述了:已批准的命令中包含的API密钥和令牌被原样存入settings.local.json。1月3日,一个自动化机器人在60天无活动后以「未计划」为由将其关闭。研究人员表示,截至今日,他们引用的全部四份相关报告均已被同一机器人关闭,最近一份是昨天关闭的,而且都没有得到Anthropic的回复。截至撰稿时,Anthropic尚未就Apiiro的发现作出公开回应。

谁来轮换,本周该审计什么

关于责任归属,不必想得太复杂:仓库的所有者拥有轮换密钥的责任。无论是智能体写入了该文件、机器人关闭了工单,还是开发者点击了工具提供的按钮,都不影响这一点。凭据属于你,它已经公开,而补救顺序是固定的——先轮换,再清理,因为历史记录和fork意味着仅仅清理只是表面功夫。可以把它看作密钥的永久死亡机制:没有读档重来,只有换一个新角色。

Apiiro面向开发者的检查清单(精简版):

  • 在你的GitHub组织中搜索.claude/settings.local.json和.claude/settings.json,并在每个仓库的git历史中搜索它们——即使文件早已被删除。
  • 在进行任何清理之前,先轮换这些文件曾经包含过的每一个密码、令牌和密钥。
  • 停止跟踪本地文件:执行git rm --cached .claude/settings.local.json,然后将其添加到仓库自身的.gitignore中。
  • 即使该文件从未被提交,也要打开你本机上的该文件,删除所有包含密钥的规则。
  • 完全避免在命令中包含密钥:引用来自密钥管理服务的变量,例如$DB_URL,使保存的规则只存储变量名。批准Bash(npm run:*)这类模式,而不是单条命令。

对于安全团队,研究人员建议将智能体文件夹——.claude/、.cursor/、.gemini/、.specstory/——以及智能体记忆文件,与.env一样视为含有密钥的文件,无论在笔记本电脑上还是在仓库中。将它们接入提交前和CI扫描(包括git历史),并使用能够识别命令行中密码的规则,而不仅限于已知密钥格式。将.claude/settings.local.json添加到组织的.gitignore模板中。在排查时按可达性排序:与公开服务器地址配对的凭据应优先处理,因为它们才是真正能打开大门的钥匙。

这里的编辑视角是对的,数据也印证了这一点:这是一个披着用户失误外衣的设计缺陷。一个工具把已批准的命令原样持久化、将结果写入项目文件、只在创建该文件的机器上保护它,有时甚至会自行提交该文件,结果仅仅为了方便,就搭建起了一条凭据外泄管道。如今63家机构因为一个按钮而不得不展开事件响应。在默认设置改变之前,这份白名单就是你的攻击面,请像对待攻击面那样审计它。

来源

本文参考了以下报道与公告: