AI 编码代理把密钥提交了:Claude Code 权限机制的隐患与补救

2 阅读5分钟

AI 编码代理把密钥提交了:Claude Code 权限机制的隐患与补救

"不再询问"很爽,直到你的密钥被推上 GitHub。

一、先还原事故:密钥是怎么"跑出去"的

据安全公司 Apiiro 于 2026 年 11 月 5 日发布的研究(外媒 11 月 7 日起陆续报道),Claude Code 这类 AI 编码代理的"不再询问 / 自动批准"权限机制,会把带明文密钥的命令留存进项目配置文件,随代码一起被提交,最终导致1,994 个公开仓库里躺着 2,539 条密钥——其中 1,122 份正是 .claude/settings.local.json 这个配置文件。

这不是某个人的操作失误,而是一条被"自动化"放大的泄漏链路:

① 你在命令里用了明文密钥(如 curl -H "Authorization: Bearer sk-...")
        ↓
② 弹窗问你"是否允许执行",你选了"Yes, and don't ask again"
        ↓
③ Claude Code 把这条含明文密钥的整条命令,写进
   .claude/settings.local.json 的 allow 规则(本意只是"下次别再问了")
        ↓
④ 这个本地配置文件被 git add、commit、push 到公开仓库
        ↓
⑤ 密钥随文件公开 → 泄漏

核心问题只有一个:本该由人拦下的高危动作,被"自动批准"直接放行了;而且这次放行的痕迹,还被持久化进了要提交的文件。

二、3个把风险放大的因素

放大器表现后果
自动批准(不再询问)Agent 执行外部命令无需确认高危操作无人把关
密钥进命令/文件拼接进 shell 命令、脚本落盘明文被写入文件或历史
批量默认提交一次提交多个文件,人没细看泄漏文件直接进仓库

3个叠加,就是"你根本没看见,它已经提交了"。

三、4道补救:从"事后救火"到"事前拦截"

补救 1:密钥只走环境变量,禁止进命令行明文

# 危险:密钥直接出现在命令里,可能被命令历史/日志记录
curl -H "Authorization: Bearer sk_live_abc123" https://api.example.com

# 安全:通过环境变量注入,命令里不出现明文
curl -H "Authorization: Bearer $API_TOKEN" https://api.example.com

配套:

  • .claude/settings.local.json 以及 .claude/ 下的本地配置必须进 .gitignore,绝不提交;团队共用的权限配置放 settings.json,且不含任何密钥;
  • .env 文件放本地密钥,必须在 .gitignore 里;
  • 给 Agent 的指令里明确写:禁止把密钥、Token 写入代码或命令行;
  • 生产环境用密钥管理服务/配置中心,代码里只有引用。

补救 2:密钥扫描进 pre-commit + CI(双保险)

靠人记不住,靠门才靠得住。推荐 gitleaks:

# 本地 pre-commit 扫描暂存区,发现疑似密钥直接拦截
# 注意:v8.19 起 protect / detect 已弃用,改用 git / dir / stdin 子命令
gitleaks git --staged --verbose

# CI 里全量扫描,作为合并门槛
gitleaks dir . --verbose    # 扫当前目录内容
gitleaks git . --verbose    # 扫 Git 历史提交

接入 pre-commit(.pre-commit-config.yaml):

repos:
  - repo: https://github.com/gitleaks/gitleaks
    rev: v8.28.0   # 以官方最新 release 为准
    hooks:
      - id: gitleaks

这样在提交那一刻就把密钥拦下,而不是等它上了 GitHub 才发现。

补救 3:权限最小化——高危操作必须人工确认

  • Agent 可以自动做读代码、改代码、跑测试这类低风险动作;
  • 但 git commit / git push / 部署 / 删除 这类不可逆或外发操作,必须人工确认;
  • 别为了省几秒钟,把关卡全关了。

补救 4:已泄漏,立刻轮换 + 清历史

一旦确认泄漏,删文件没用——Git 历史里还在。

# 1) 第一时间去平台吊销 / 轮换密钥(最优先)
# 2) 用 git filter-repo 清理历史中的敏感文件
git filter-repo --path path/to/secret.env --invert-paths
# 3) 强制推送并通知协作者重新拉取
git push --force-with-lease

顺序千万别错:先吊销,再清历史。密钥只要还有效,清历史也白搭。

历史重写后还有两件事:通知所有协作者重新 clone(旧的本地历史会把它带回来),以及处理 GitHub 上已存在的 fork / 缓存副本、已合并的 PR 引用——上游干净了,别人手里的副本未必。

补救之外:先自查有没有中招

  • 在自己的公开仓库里搜 .claude/settings.local.json,或直接搜 Bearer、sk- 这类特征串;
  • 本地对全量历史扫一遍:gitleaks git .;
  • 打开 .claude/settings.local.json,看 allow 规则里有没有你当初点过"不再询问"的外发命令。

四、AI 编码代理安全使用清单(可直接照做)

  1. 默认关闭"自动批准高危操作"——提交/推送/部署必须人工确认
  2. 密钥只走环境变量,.env 与 .claude/settings.local.json 一起进 .gitignore
  3. pre-commit + CI 双保险扫 secret(gitleaks / trufflehog)
  4. 提交前必看 diff——AI 批量提交尤其要逐文件过一眼
  5. 给 Agent 写安全边界(类似 AGENTS.md):禁止输出密钥明文、禁止自动推送
  6. 泄漏即轮换,再用 git filter-repo 清历史
  7. 定期审计——查一遍公开仓库里有没有自己的密钥

五、写在最后

AI 编码代理的便利和风险是一体两面:它越"自动",越可能在你看不见的地方替你做了不该做的事。

"不再询问"不该等于"不再把关"。 权限可以交给 AI,但安全这条线,永远得由人来兜底——用规则、用门控、用扫描,把它拦住。

省下的那几次确认弹窗,换来的是一个上 GitHub 的密钥,不值。

参考来源

  1. Apiiro 研究(2026-11-05):Claude Code "Yes, and don't ask again" 将含密钥命令持久化至 .claude/settings.local.json allow 规则,共发现 2,539 条密钥、1,994 个公开仓库
  2. The Hacker News《Claude Code's "Don't Ask Again" File Exposes Passwords and Keys in 1,122 Public Copies on GitHub》(2026-11-07):thehackernews.com/2026/11/cla…
  3. Cyber Security News(2026-11-07):cybersecuritynews.com/claude-code…
  4. gitleaks 官方文档(git / dir 子命令,v8.19 起 protect / detect 弃用):github.com/gitleaks/gi…