一句话总结:Claude Code 会把用户批准过的 shell 命令逐字存进本地 .claude.json,误提交到公开仓库就泄露真实凭据,研究人员在 GitHub 上确认了 1,122 个案例。我照着这个模式写了个扫描器:20 个真泄露抓到 14 个,12 个安全样本零误报,但两个看似无害的正则写法各吃掉了一类泄露。
11 月 9 日,安全厂商 Apiiro 公布了这个发现。11 月 10 日发布的 Claude Code 2.1.321 修了一批权限和凭据泄漏问题,包括 MCP 的 OAuth 那部分。
Claude Code 凭据泄露是怎么发生的
Claude Code 为了减少重复询问,会把用户批准过的命令记在本地配置里,下次遇到同样的命令就直接执行,不再问。
问题在于记的是原文。一个批准过的命令如果是 export DB_PASSWORD=Pr0d_2026!x,那这一行就原样落在 JSON 里。
.claude.json 通常放在项目根目录或用户目录。前者很容易被 git add . 带进去——很多人没有把这个文件名写进 .gitignore,因为它看起来不像配置文件,更像编辑器缓存。
Apiiro 在公开的 GitHub 文件里数出 1,122 个真实凭据。
凭据泄露扫描器实测:20 个抓到 14 个
我按这个模式造了 32 个样本:20 个真泄露(8 种写法,覆盖 password、API key、token、client_secret、JWT),12 个安全样本(读环境变量、空字符串、占位符、提示输入)。
核心规则一条:键名像凭据 + 赋值了一个双引号字符串。
RULE = re.compile(
r'(?i)\b(password|passwd|pwd|secret|secret_key|api[_-]?key|apikey'
r'|token|access_token|auth_token|client_secret)\b'
r'\s*[:=]\s*["\']([^"\']{6,})["\']'
)
PLACEHOLDER = re.compile(r'(?i)^(x+|\*+|changeme|your|example|test|demo|password|null|none|<.*>|\$\{.*\})')
结果:
| 实际是泄露 | 实际安全 | |
|---|---|---|
| 判定为泄露 | 14 | 0 |
| 判定为安全 | 6 | 12 |
准确率 81.2%,精确率 100%,召回率 70%。零误报是我把占位符黑名单写得很宽换来的,代价就是漏报。

图 1:32 个样本上的判定结果,漏报集中在两类写法
两个 bug:一个吃掉 X 开头的密码,一个让带前缀的键名溜过去
漏掉的 6 个里,5 个能归到两个原因。
第一个:^x+ 把 X 开头的密码当成占位符。
占位符黑名单里我写了 x+,本意是过滤 "xxxxxxxx" 这种假密码。但正则的 x+ 匹配"一个或多个 x",^x+ 只要开头是 x 就算命中。于是 password = "Xk9#mP2$vL7q" 被判定为占位符直接跳过——这个密码强度还挺高的,恰恰因为首字母是 X 逃过一劫。
正确写法要加长度约束:^x{6,}$,要求整串都是 x。
第二个:\b 在 db_password 上不成立。
正则里的 \bpassword\b 要求词边界。但下划线属于单词字符,db_password 里 _ 和 p 之间没有边界,所以匹配失败。同理 DB_PASS、MY_SECRET 这些带前缀的键名全部漏掉。
改法是去掉前导 \b,或者在键名前面允许下划线:(?<![a-z0-9])password(允许前面是下划线,不允许前面是字母数字)。
这类 bug 有个共同特点:写的时候觉得"规则已经很全了",跑完发现漏的都是最常见的写法。
凭据泄露自查:文件名规则比内容规则更有效
同一批样本,如果只按文件名筛——.claude 目录、*.env、config*——20 个真泄露全都能捞到,一个不漏。
这个结果不奇怪。凭据泄露的载体比凭据本身更稳定:不管里面写的是 password 还是 token,文件名和路径就那么几种。
所以实际做防护,顺序应该反过来:
先管住目录,把 .claude/、.claude.json、.env*、*.pem 写进 .gitignore,再加一条 pre-commit 钩子拦住这类文件。这一步的成本接近零,覆盖率最高。
再管内容,用正则兜底,用来抓那些被重命名、或者塞进 notes.txt 里的凭据。这一步必然有漏报,别指望它。
最后做回溯,如果这些文件已经被提交过,光删文件没用,git 历史里还在。要用 git filter-repo 清历史,然后立刻轮换所有可能泄露的凭据。轮换是必须的,清理历史只是止损。
agent 的记忆正在变成新的攻击面
同一批研究还提到另一个方向的漏洞:提示注入可以诱导 agent 取回被明确要求删除的数据,绕过零信任架构里设好的清除检查点。
两件事指向同一个结论:agent 的"记忆"和"批准记录"正在成为新的凭据攻击面,而这部分能力通常是在追求自动化流畅度的过程中被顺手打开的。
设计 agent 的时候有个默认值值得先定下来:本地状态目录里到底允不允许存明文?如果允许,那它必须被当作凭据对待——不进版本控制、不随项目分发、有轮换机制。之前拆 Ollama 的仓库结构时也遇到过类似的问题:模型文件按哈希命名,删了 manifest 之后 blob 还留在磁盘上,占着几十 GB 没人知道。
上生产之前,花五分钟检查一下项目里有没有 .claude 之类的目录已经进了版本控制。文件头部的元信息怎么读是另一套技能,但道理一样:先看清楚文件里到底存了什么,再决定能不能公开。
参考资料
- 参考资料
发表评论
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。