提交前防止把密钥写进 Git 仓库
写代码时临时把 token 硬编码进一个测试文件,想着「待会儿删」,
结果一忙就忘了,git add . 加 commit 一起带上去。
这件事的麻烦之处在于:提交之后删掉也没用,历史里那份对象还在。
为什么删了也不算完
Git 保存的是内容对象(blob),只要那个 commit 还在可达范围内,
对应的 blob 就一直存在于 .git 里。
即使你新建一个 commit 把密钥删掉,旧 commit 依然可以通过 SHA 访问到。
force push 也只能改远端分支指针,远端仓库的旧对象不会立刻被回收, 中间还有一个可以被访问的窗口期。如果仓库是公开的, 这个窗口期通常足够被自动化爬虫扫到。
所以正确顺序永远是:先别提交上去,而不是提交之后再想办法清理。
第一道防线:.gitignore 要在最前面
项目初始化第一件事就是写 .gitignore,把可能装密钥的文件先挡掉:
.env
.env.*
!.env.example
*.pem
*.key
secrets/
credentials.*
config.local.*
保留一个 .env.example 作为模板,里面只写变量名和说明,
值留空。这样别人(或者几个月后的自己)知道需要配哪些项,又不会把真实值带进去。
第二道防线:提交前扫一遍暂存区
关键是用 git diff --cached 而不是 git diff——
前者只看已经 add、准备提交的内容,这才是真正会进仓库的东西。
git diff --cached | grep -nE 'sk-[A-Za-z0-9]{20,}|ghp_[A-Za-z0-9]{30,}|AKIA[0-9A-Z]{16}|BEGIN [A-Z ]*PRIVATE KEY'
没有输出就是干净的。有输出就说明有问题,这时候 git reset 撤回暂存,
改完再来。
常用的密钥前缀值得记一下,扫的时候直接按前缀匹配,误报少、速度快:
| 前缀 | 对应服务 |
|---|---|
sk- | 各类大模型 / OpenAI 风格 API |
ghp_ / github_pat_ | GitHub 个人访问令牌 |
AKIA | AWS Access Key ID |
glpat- | GitLab 令牌 |
xoxb- | Slack Bot Token |
第三道防线:pre-commit 钩子
靠人记住检查是不可靠的。在 .git/hooks/pre-commit 放一个脚本,
命中就拒绝提交:
#!/bin/sh
# 拒绝提交疑似密钥的内容
if git diff --cached | grep -qE 'sk-[A-Za-z0-9]{20,}|ghp_[A-Za-z0-9]{30,}|BEGIN [A-Z ]*PRIVATE KEY'; then
echo "pre-commit: 暂存区里疑似包含密钥,已阻止提交。" >&2
echo "请先移除相关内容,再重新 add。" >&2
exit 1
fi
记得 chmod +x .git/hooks/pre-commit,否则不会执行。
被钩子拦住是正常现象,不要用 --no-verify 绕过——
绕过的恰恰是唯一起作用的那道关。
如果已经提交上去了
处理顺序是「先作废,再清理」,而不是反过来:
- 立刻到对应平台把这个密钥吊销 / 轮换,这一步最重要,也最快
- 再考虑用
git filter-repo之类工具重写历史 - 重写后通知所有协作者重新克隆,旧 clone 里的历史仍然存在
很多人第一步就去做历史重写,花了一小时清理完,密钥其实早就被扫走了。 作废只要一分钟,先做这个。
小结
三道防线各有各的用:.gitignore 挡住大部分手滑,
提交前 grep 兜住漏网的,钩子保证不依赖人的记性。
三样加起来成本不到十分钟,比事后清理省太多。