AI coding · 一个勾选框,把一千三百七十九个秘密留在了公开仓库里
公司前台放一本登记本,访客报一次口令,门卫问要不要以后都放行。勾了"以后别再问",登记本上就把这位访客连同他报的口令一起抄了进去。本子放在项目目录里,谁 clone 一次谁翻得到。11 月 9 日 The AI Signal 那篇深度稿讲的 Claude Code 凭证问题,机制就是这么朴素。
一千三百七十九是秘密数,一千一百二十二是文件数,两个数之间还有历史版本和分叉。
公司前台放一本登记本,访客报一次口令,门卫问要不要以后都放行。勾了"以后别再问",登记本上就把这位访客连同他报的口令一起抄了进去。本子放在项目目录里,谁 clone 一次谁翻得到。11 月 9 日 The AI Signal 那篇深度稿讲的 Claude Code 凭证问题,机制就是这么朴素。
Apiiro 在公开 GitHub 上数出 104,185 份 .claude/settings.local.json,分布在 103,617 个仓库。其中 1,122 份至少含一个秘密,合计 1,379 个不同的秘密。数字出自 The Hacker News 11 月 7 日的报道。Apiiro 自己卖 coding agent 的安全工具,读这份统计时把这一层立场记上。
一次勾选写下了什么
Claude Code 跑多数 shell 命令前会问一次,选项里有"Yes, and don't ask again"。选了它,Claude Code 就在项目里写一条 allow rule 进 .claude/settings.local.json,下次同样命令不再问。提示若只给一条确切命令,没给模式,规则就是那条命令逐字,密码或 token 若写在命令里,也一并被存成纯文本 JSON。研究者用 Opus 5.5 与 Fable 5.1 复现了这个行为,The AI Signal 说机制在 Claude Code 自己的权限文档里写得清楚。
Claude Code 有一层保护,但只盖住一台电脑。第一次在某个仓库写这个文件时,它把文件名加进用户全局 git excludes,本机上的 git 于是到处都忽略它。项目的 .gitignore 从来没被写进去,仓库本身没有防护;已经提交过的文件,全局排除也管不着。Apiiro 的读法更扎人:在他们能追溯的 1,105 个仓库里,有 539 个出现"Claude 作为 co-author"的提交改过这个文件。
秘密的构成也具体。超过三分之一能直接打开数据库,其中 379 个是数据库密码;另有 43 个公网服务器的 SSH 登录,42 个 GitHub 与 GitLab 及 npm 的 token,23 个 AWS 密钥;63 个秘密的所有者是组织,多数为软件公司。Apiiro 没有测这些密钥是否仍然有效,只有一家被通知的公司确认自己的 key 当时是活的。
四个数各有各的分母
这篇材料最容易读岔的地方,是把几组数当成同一批东西的逐级放大。
| 数值 | 口径 | 出处条件 |
|---|---|---|
| 1,122 | 含至少一个秘密的公开文件数 | 103,617 个仓库、104,185 份文件里筛出 |
| 1,379 | 上述文件里的不同秘密数 | 同一批文件的当前版本 |
| 734 / 509 | 历史版本里另有 734 个秘密,跨 509 个仓库 | 删掉当前文件也清不掉 |
| 2,539 / 1,994 | 合计秘密数与涉及仓库数 | 再并进共享设置文件、旧版本、保存的 agent 聊天记录 |
.claude/settings.json 在 76,011 份里有 298 份含秘密。截至 11 月 2 日,每一个含秘密的文件都还在仓库默认分支上,其中半数至少已经放了 217 天。把这些数并排写成"泄露规模从 1,379 涨到 2,539",等于把当前版本与含历史及聊天的总量当成了时间上的增长。扫描器为什么漏掉五分之一
研究者拿 gitleaks 和 TruffleHog 跑遍所有副本,未启用工具的验证步骤。1,122 份含秘密的文件里,gitleaks 标出 564 份,TruffleHog 标出 643 份,两者都没标出的是 215 份,约五分之一。The Hacker News 给的解释直接:扫描器找的是已知密钥形状,而跟在 PGPASSWORD=、sshpass -p 或 mysql -p 后面的密码,看起来就是一条普通命令行里的普通词。
他们另外比了 16 个 coding agent 的文档,Claude Code 是唯一确认默认把完整批准命令存进项目文件的一个。Apiiro 仍建议安全团队把别的 agent 目录一并扫。用户其实早报过:2025 年 8 月 11 日的一份 bug report 就写明,带 API key 与 token 的批准命令被逐字存进 settings.local.json;2026 年 1 月 3 日一个自动 bot 以"not planned"关掉了它,理由是 60 天无活动,研究者引用的另外四份相关报告也被同一个 bot 关掉,Anthropic 没有回复。
处置顺序在稿件里排得很清楚,先轮换再清理。曾经提交进公开仓库的秘密,从那次提交起就算暴露,删文件留在历史与分叉里。稿件给的动作是:搜组织内两个设置文件并覆盖 git 历史,轮换文件里出现过的每个口令,用 git rm --cached 停止跟踪并写进项目自己的忽略名单,把本机文件里带秘密的规则删掉。往后的写法更省事,命令里只写变量名,值交给密钥管理器;批准 Bash(npm run:*) 这类模式,别去批准单条命令。安全团队那一条是把 .claude/ 与 .cursor/ 两个目录,连同 .gemini/ 和 .specstory/ 一起当 .env 对待,纳入含历史的提交前与 CI 扫描。
费曼大概会问:门卫为什么要把口令抄进登记本?因为提示框问的是"这条命令还问不问",回答被存成了"这条命令长什么样"。两个问题看着一样,落到文件里差着一整串字符。这个勾选框按字面完成了它的承诺,停下来不再问;它顺手多存的东西,项目的忽略名单里一个字都没提。
同一天还翻到
| 方向 | 线索 | 状态 |
|---|---|---|
| AI coding | The AI Signal 索引:Claude Code v2.1.321 把 Fable 5.2 设为默认 Fable 模型,并补了远程控制设置文件与若干权限沙箱修复 | 11 月 10 日条目,未作主篇 |
| AI coding | Cursor 项目功能支持多人协作,共享任务看板与共享上下文层,团队成员的智能体之间可直接通信 | 官方博客,页面未标日期 |
| 游戏引擎 | 本站 11 月 11 日已写 2004 年赛车游戏在浏览器里重新上线,机器码被逐函数翻译成 C | 见 178636963,本轮不重复 |
#AI编程 #凭证泄露 #供应链安全 #口径核对