适用对象:在个人开发或小团队中使用 Git、担心「会不会不小心把 API 密钥提交上去」的人。本文不讲攻击方的手法,只讲阻止机密从自己的仓库流出的机制。
本站的看法:光靠 .gitignore 守不住机密
常听到有人说「我把 .env 放进 .gitignore 了,所以没问题」,但这会漏掉一些情况。.gitignore 只是不让新文件被追踪,对已经提交的机密、赶时间用 git add -f 强制添加的机密、以及 .env.local.bak 这种意料之外的文件名都抓不住。它只能防住机密混入的部分途径。还需要一个真正查看提交内容、标记「看起来像机密的字符串」的东西,也就是检测器。这正是 gitleaks 的用途。
为什么要在「提交前」拦住
机密之所以危险,是因为 Git 会永久保留历史。即使从最新文件里删掉,过去的提交里还留着。而在 push 的那一刻,这段历史就会被复制到其他机器、服务器和 CI 日志里。
所以机密防护的重点其实是泄露前的预防,而不是泄露后的补救。这和「别把机密留在公开目录里」的盘点(→ 公开目录的盘点)是同一个思路,只是用在了代码上。
在哪些环节检出机密
在机密从代码流向外界的路径上,可以设两处检查:本地提交的时候,以及被共享之前(CI)。
代码里的机密
API 密钥、私钥、token
检查 1:pre-commit
提交时在本地检出并中断
检查 2:CI / cron
push 后在合并/部署前检出
公开/共享
到这一步就要按已泄露处理
如何集成 gitleaks
先扫描现有历史
gitleaks git(v8.19.0 之前的版本为 gitleaks detect)把整个仓库的历史扫描一次。找出过去提交里留下的机密是第一件事。这里找到的东西,如下文所述,都要以吊销为前提。用 pre-commit 钩子加上检查 1
gitleaks git --pre-commit --staged(以前是 gitleaks protect --staged),当场中断包含机密的提交。把它加进 pre-commit 框架(.pre-commit-config.yaml),所有人在本地就会运行同样的检查。用 CI / cron 加上检查 2
gitleaks git,自建环境则作为定期扫描运行,抓住漏掉的东西(和依赖审计一样的自动监控思路)。在 .gitleaks.toml 中调整误报
.gitleaks.toml 中调整规则和 allowlist。正确的做法不是「压掉警告」,而是「记录为什么它是安全的」,没有依据的排除可能引发下一次事故。检出后先吊销,改写历史在后
发现已被提交并 push 的机密时,最优先的是吊销并重新签发(轮换)那个密钥或 token。用 git filter-repo 从历史中删除固然重要,但那只是「不让它继续扩散」的善后。要假定已经到达公开仓库、fork、CI 日志或他人 clone 的机密无法收回:先让它失效,再清理历史。顺序反了,你还在删除的时候它就可能已经被人使用了。
只靠 .gitignore
- 只能防止新的追踪
- 已经提交的机密检不出来
- 对
git add -f或奇怪的文件名无能为力 - 无法验证「我以为没加进去」
gitleaks(检出+吊销流程)
- 实际检查工作树和历史的内容
- 用 pre-commit 和 CI 两处检查拦住
- 检出后有一套直到吊销为止的流程
- 误报记录依据后再排除
该用哪个命令(v8.19.0 及以后)
gitleaks 在 v8.19.0 中重新整理了命令。旧的 detect 和 protect 仍能运行,但已被弃用,也不再出现在帮助中。新配置时请使用下面这些:
| 命令 | 扫描对象 | 典型用途 |
|---|---|---|
gitleaks git | Git 提交历史 | 首次全历史扫描、CI、定期扫描 |
gitleaks git --pre-commit --staged | 即将提交的更改(已暂存) | pre-commit 钩子(官方钩子运行的正是这条) |
gitleaks dir | 文件夹和文件,包括不在 Git 中的 | 公开网页目录、备份、解压后的发布 zip |
gitleaks stdin | 通过标准输入传入的文本 | 其他命令的输出或日志 |
gitleaks 发现至少一处泄露时以退出码 1 结束,没有发现时为 0,所以 CI 和 cron 任务可以根据退出码判定失败。加上 --redact,机密本身就不会出现在输出或日志里。
配置示例:pre-commit 钩子与 GitHub Actions
使用 pre-commit 框架时,把下面的文件放在仓库根目录。添加时把 rev 设为最新版本(截至 2026 年 10 月为 v8.30.1)。
# .pre-commit-config.yaml
repos:
- repo: https://github.com/gitleaks/gitleaks
rev: v8.30.1
hooks:
- id: gitleaks除了 gitleaks(用 Go 构建)之外,id 还可以是 gitleaks-docker(在 Docker 中运行)或 gitleaks-system(使用机器上已安装的 gitleaks)。然后每个人在自己的机器上运行:
pip install pre-commit # install pre-commit itself
pre-commit install # register the hook in this repo (once per clone)
pre-commit run --all-files # check every file once, right after registering
pre-commit autoupdate # run now and then to move rev to a newer release在 GitHub 上做 CI,可以使用官方的 GitHub Action。它需要完整的历史,所以用 fetch-depth: 0 进行 checkout。
# .github/workflows/gitleaks.yml
name: gitleaks
on:
pull_request:
push:
schedule:
- cron: "0 4 * * *"
jobs:
scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
- uses: gitleaks/gitleaks-action@v3
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITLEAKS_LICENSE: ${{ secrets.GITLEAKS_LICENSE }}对于组织账户拥有的仓库,这个 Action 需要许可证密钥(GITLEAKS_LICENSE)。官方 README 说明该密钥可以从 gitleaks.io 免费获取,个人账户不需要。在自建 Git 服务器或 GitHub 以外的 CI 上,在 job 中运行 gitleaks git --redact 也能达到同样效果。
在哪里检查:四个位置的比较
任何单独一处检查都会留下漏洞。把每一处能拦住什么、在哪里会漏整理出来,就能看出为什么要组合使用。
| 位置 | 拦截时机 | 覆盖范围 | 漏洞和限制 |
|---|---|---|---|
| pre-commit 钩子(本地) | 提交之前 | 已暂存的更改 | 用 git commit --no-verify 或 SKIP=gitleaks 就会跳过。clone 仓库不会安装钩子 |
| CI(GitHub Actions 等) | 合并或部署之前 | 已推送的提交和历史 | push 已经发生。公开仓库的话要按已泄露处理 |
| 定期扫描(cron) | 事后发现 | 整个仓库的历史 | 发现有延迟。能抓到新规则在旧提交中发现的东西 |
| GitHub push protection | 推送到 GitHub 时 | GitHub 支持的机密类型 | 给出理由就能绕过。对其他 Git 服务器不起作用 |
面向用户的 GitHub push protection 对推送到公开仓库默认开启。它是免费的安全网,但拦不住本地提交,不支持格式的密钥或手工拼出的连接字符串也可能漏过(详见 为什么在 GitHub 上暴露的凭据仍然有效)。gitleaks 覆盖的是它之前的那一步,以及 GitHub 以外的一切。
如何检查自己的环境
配置过一次,不代表它在运行。在自己的仓库里按顺序检查以下几点。
确认 gitleaks 的版本
运行 gitleaks version。早于 v8.19.0 的版本没有 gitleaks git 等新命令,需要更新。
确认钩子已注册
查看是否存在 .git/hooks/pre-commit。如果 git config --get core.hooksPath 指向其他文件夹,就去那里查看。clone 时 Git 不会复制钩子,所以在新机器或新的 clone 上要再次运行 pre-commit install。
把整个历史扫描一次
在仓库根目录运行 gitleaks git --redact -v。没有检出时退出码为 0。如果有检出,根据输出中的文件名和提交,到签发方的控制台确认该密钥是否仍然有效。
也扫描 Git 以外的文件夹
服务器上的公开目录、备份以及要分发的 zip 的内容,在 Git 历史扫描中都看不到。用 gitleaks dir (path to folder) --redact 检查它们(→ 公开目录的盘点)。
确认 .env 是否被提交过
运行 git log --all --oneline -- .env,查看过去是否有提交涉及 .env。只要出现一行,那个时间点的内容就在历史里。
误报的处理
测试用的假密钥和文档中的示例经常会被标记。排除的方法有三种,使用能解决问题的最窄的一种。
- 按行:在那一行加上
gitleaks:allow注释 - 按检出结果:把输出中显示的指纹加进仓库根目录的
.gitleaksignore(README 将其标为实验性功能) - 按文件或模式:加进
.gitleaks.toml的 allowlist
如果自己编写 .gitleaks.toml,在 [extend] 下写上 useDefault = true 以保留默认规则。只有自定义规则的配置,会悄悄漏掉默认规则本来能抓到的密钥。
如果旧仓库里已有大量检出结果,可以把过去的报告传给 --baseline-path,只报告新的检出。但要先吊销那份报告里的密钥。把密钥放进 baseline 并不会让它变安全。
常见错误及修正方法
- 以为有配置文件就够了:在有人运行
pre-commit install之前,钩子什么也不做。让每个人都运行,并在 CI 中运行同样的检查作为第二层。 - 习惯了使用
--no-verify:把 CI 检查设为合并的必要条件,这样被跳过的钩子也能被抓住。 - 把机密打印到 CI 日志里:加上
--redact。CI 日志也是很多人都能看到的。 - 在 CI 中扫描浅克隆:只会检查最新的提交,历史中的机密会被漏掉。设置
fetch-depth: 0。 - 从历史中删除后就结束:GitHub 自己的指南把吊销或轮换机密作为第一步。fork、clone 和缓存中的副本无法删除。
- 把真实的密钥当作「误报」加进 allowlist:在注释中写明理由,并在评审时检查。
真实案例:访问密钥被公开放置
2022 年 10 月 7 日,丰田汽车公司宣布,其联网汽车服务 T-Connect 用户网站的部分源代码曾在 GitHub 上公开。根据该公司的公告:
- 2017 年 12 月,负责开发该网站的委托方误将部分源代码上传到设置为公开的 GitHub 账户
- 代码中包含存储客户信息的数据服务器的访问密钥
- 代码于 2022 年 9 月 15 日改为非公开,访问密钥于 9 月 17 日更换
- 涉及 296,019 个电子邮件地址和客户管理编号。访问日志中没有发现第三方使用的痕迹,但公司表示无法完全排除这种可能
从上传到删除,大约过了五年。而且这发生在公司自己的仓库之外,在委托方的账户里。对自己仓库的扫描无法覆盖代码最终可能去的所有地方,所以还要限制每把密钥能做的事,并定期更换密钥。即使在这个案例中,密钥也是在代码下架两天后才更换的:发现之后,就要更换密钥。
私有仓库也不是存放机密的安全地方。在 CAMPFIRE 2026 年的案例中,据该公司称,被滥用的 GitHub 凭据是起点,在 GitHub 上可以查看的信息被用来获取云服务凭据(→ CAMPFIRE 入侵事件(英文))。私有仓库中也不要放机密,并定期用 gitleaks 扫描。
本站是怎么做的
本站的基本规则是机密一概不放进 Git:连接字符串和 API 密钥只放在服务器上受保护的配置里(权限受限的 env 文件),仓库和交接资料中都不留明文(→ 不把机密放进 git/自建 Git vs GitHub)。在此之上,再以人一定会犯错为前提叠加检测器。用设计保证「不放进去」,用 gitleaks 这类扫描抓住「还是放进去了」,这两层就是本站防止机密泄露的做法。处理机密的原则本身,与 .env 文件与机密 以及 安全保存密码 相同。
接下来阅读
- GitHub 设置:如何设置 GitHub 双因素认证
- 基础:.env 和 API 密钥:会出什么问题,如何保护
- 从 GitHub 凭据到入侵:CAMPFIRE 入侵事件(2026,英文)
- 泄露之后:在 GitHub 上暴露的 543,699 个凭据仍然有效:为什么要吊销而不只是删除
- 无人操作的攻击:Hugging Face 入侵事件与防守方必须放弃的假设
- 盘点:有没有把机密留在公开目录里
- 自建运维:不把机密放进 git/自建 Git vs GitHub
- 术语:什么是「.env」:环境变量文件泄露会发生什么
- 工具:机密泄露检查(粘贴即可检查)
参考资料
- gitleaks(官方仓库)README:命令(git、dir、stdin)、v8.19.0 中 detect/protect 的弃用、退出码、
--redact、gitleaks:allow、.gitleaksignore、--baseline-path、配置扩展 — github.com/gitleaks/gitleaks / 官方钩子定义 — .pre-commit-hooks.yaml / Releases - gitleaks-action(官方 GitHub Action)README:工作流示例与许可证密钥 — github.com/gitleaks/gitleaks-action
- pre-commit 文档(install、run --all-files、autoupdate、SKIP) — pre-commit.com
- Git 文档「githooks」(pre-commit 钩子与 --no-verify) — git-scm.com
- GitHub Docs「About push protection」 — docs.github.com / 「Removing sensitive data from a repository」 — docs.github.com
- 丰田汽车公司,关于客户电子邮件地址可能泄露的通知(2022 年 10 月 7 日,日文) — global.toyota