gitleaks 实测:把误提交的密钥挡在 commit 之前——Windows 本地全流程记录
gitleaks 实测:把误提交的密钥挡在 commit 之前——Windows 本地全流程记录
.env 手滑 git add -A、示例代码里粘了真 token、CI 配置里写死了 AK。密钥误提交是最高频也最便宜的安全事故:一条 commit 就把凭证送进 git 历史,删文件没用,clone 过的人都有。gitleaks(gitleaks/gitleaks,约 3 万星,MIT)是这块最常用的开源方案:一个 Go 单二进制,内置 222 条密钥规则,能在 commit 前和 CI 里把泄露卡住。
我在 Windows + Git Bash 下把它完整跑了一遍:自造假 AWS key、假 GitHub token、本地生成私钥造了一个测试仓库,detect / protect / JSON 报告 / .gitleaks.toml 全部实跑。这篇是全过程记录,所有输出都是真实截取。
一、它解决什么问题
git 历史是不可变的。密钥一旦进过 commit,后续 git rm、改文件都只是新增 commit,旧 blob 还在,任何 clone 都能 git log -p 翻出来。公网上的自动化扫描器会持续扫 GitHub 找这类凭证,很多误提交的密钥在推送后很短时间内就被扫到并利用。
gitleaks 的定位是在密钥进入历史之前拦住它,三个入口对应三个时机:
- pre-commit:
gitleaks protect --staged只扫暂存区,秒级返回,挡住手滑的git add; - CI:同一条命令放进 CI,发现泄露就让检查失败(它靠退出码通信,见坑 3);
- 历史自查:
gitleaks detect(等价新命令gitleaks git)走完整提交历史,仓库开源前、接手老仓库时跑一次。
它只做「检测」这一件事:扫出来、报规则名和位置、给非零退出码。不托管密钥、不改代码。所以它轻,也意味着它替代不了密钥管理。
二、核心概念:规则、熵与 allowlist
理解三件事,用起来就不会慌:
- 规则 = 正则 + 关键词 + 熵阈值。每条规则先靠关键词粗筛(比如文件里出现
AKIA才进入 AWS 规则),再正则匹配,最后看熵(随机程度)够不够。熵阈值的存在是为了放过AKIAAAAAAAAAAAAAAAAA这类低随机串(实测同文件里 16 个 A 不命中、AKIA2XQP7FZLMN4RKT5V命中); - 退出码即结论:扫出泄露退出码为 1,干净为 0,这是给 CI 和命令行消费的契约;
- allowlist 收敛误报:测试固件、文档示例值、已整改的历史 commit,可以通过
.gitleaks.toml的[allowlist](按路径/按正则/按 commit)或行尾注释# gitleaks:allow显式豁免,而不是把规则关掉。
三、我的实测:从安装到命中
安装
Releases 页下 Windows x64 zip(v8.30.1 约 8.4 MB),解压即得 gitleaks.exe + LICENSE + README,无运行时依赖:
$ ./gitleaks.exe version
8.30.1
造数据
建临时 git 仓库,先提交三个文件(密钥全部自造/本地生成,从未使用):
deploy.env:AWS 官方文档示例 keyAKIAIOSFODNN7EXAMPLE+ 配套 secret;ci.token:GITHUB_TOKEN=ghp_DEMOfake1234567890abcdefghijklmnopqrstuvwx;config/sign.pem:ssh-keygen本地生成的 RSA 私钥。
演示过程中又补了三个文件:deploy2.env 放一把非示例的随机假 AWS key(AKIA2XQP7FZLMN4RKT5V 加 42 位假 secret),这是第四节里 aws-access-token 与 generic-api-key 两个真实命中的来源;ci2.token 放同一个 GitHub token 但行尾带 # gitleaks:allow,用来演示单行豁免;internal.conf 放一把内部格式 token,演示自定义规则。
扫描历史
gitleaks detect --source . -v,真实输出(节选,私钥内容略去):
Finding: -----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAuZX3k5kxMi1B+IY1MtUVc6pA3CO/6p7LtOAWfWh3tyDzOcDU
pzo...
Secret: -----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAuZX3k5kxMi1B+IY1MtUVc6pA3CO/6p7LtOAWfWh3tyDzOcDU
pzo...
RuleID: private-key
Entropy: 6.022935
File: config/sign.pem
Line: 1
Commit: 86ac30a42467b91ebc3f4ec9a33dcec0664150f8
Author: gitleaks-demo
Email: demo@example.com
Date: 2026-10-14T01:03:10Z
Fingerprint: 86ac30a42467b91ebc3f4ec9a33dcec0664150f8:config/sign.pem:private-key:1
Finding: GITHUB_TOKEN=ghp_DEMOfake1234567890abcdefghijklmnopqrstuvwx
Secret: ghp_DEMOfake1234567890abcdefghijklmnopqr
RuleID: github-pat
Entropy: 4.971928
File: ci.token
Line: 1
Commit: 86ac30a42467b91ebc3f4ec9a33dcec0664150f8
9:03AM INF 1 commits scanned.
9:03AM INF scanned ~1841 bytes (1.84 KB) in 911ms
9:03AM WRN leaks found: 2
两个真实的细节:
- 官方示例 key 没被报。
AKIAIOSFODNN7EXAMPLE在默认配置的 allowlist 里(我用一个非示例的随机假 key 才触发aws-access-token规则),自造测试数据时别直接抄文档示例,否则测了个寂寞; - 每条命中都有 Fingerprint(
commit:file:rule:line),这是 allowlist 按条豁免的钥匙。
pre-commit 场景
新写一个含 Slack webhook 的文件,git add 后扫暂存区:
$ git add staged.txt
$ ./gitleaks.exe protect --staged --source . --no-banner
9:09AM INF 0 commits scanned.
9:09AM INF scanned ~92 bytes (92 bytes) in 504ms
9:09AM WRN leaks found: 1
-v 下能看到 RuleID: slack-webhook-url、File: staged.txt、Line: 1。注意 0 commits scanned。protect 扫的是索引不是历史,所以快(504ms)。
报告导出
接 CI 或事后审计用 JSON:
$ ./gitleaks.exe detect --source . --no-banner --report-format json --report-path report.json
每条 finding 的字段(node 解析验证过):RuleID、Description、StartLine、EndLine、StartColumn、EndColumn、Match、Secret、File、Commit、Entropy、Author、Email、Date、Message、Tags、Fingerprint。注意行号字段叫 StartLine 不是 Line(-v 终端输出里才叫 Line),写结果解析时别踩。
四、配置与进阶:.gitleaks.toml 把命中从 14 处收敛到 5 处
误报是这类工具落地最大阻力。我的测试仓库后来混进了两类噪声:fixtures/legacy.dump(故意放的假 key 固件)和 report.json(上一节的扫描报告,本身含密钥内容)。无配置扫描 14 处命中:8 处来自 report.json 自己,2 处来自 fixtures 固件,剩下 4 处是真实秘密。
在仓库根写 .gitleaks.toml(扩展默认规则 + 路径豁免 + 一条自定义规则):
title = "blog-demo gitleaks config"
[extend]
useDefault = true
[allowlist]
description = "allow test fixtures and scan report files"
paths = [
'''^fixtures/''',
'''report\.json$''',
]
[[rules]]
id = "internal-token"
description = "Internal service token"
regex = '''INTERNAL-[A-Z0-9]{10}'''
entropy = 1
tags = ["internal"]
再次扫描,真实输出:
11:29AM INF 8 commits scanned.
11:29AM WRN leaks found: 5
fixtures 与 report.json 的 10 处噪声全部消失,真实泄露保留 4 处(私钥、GitHub token、AWS key、generic-api-key 各 1 处),加上自定义规则命中 internal.conf 的 1 处,合计 5 处。自定义规则也验证生效:把 INTERNAL-TOKEN9KXZQ 写进非豁免文件 internal.conf,命中 RuleID: internal-token。
三个配套开关也实测可用:
--enable-rule internal-token:只跑指定规则,输出leaks found: 1,调试规则时省事;--redact=100:所有Secret:字段输出REDACTED,CI 日志不外泄;- 行尾
# gitleaks:allow:单行豁免,适合「这行确实是测试值」的场景(实测带注释的行不再命中)。
五、踩坑实录(Windows,全部实测)
detect/protect在 v8.30.1 顶层帮助里不可见:gitleaks --help只列git/dir/stdin等,但gitleaks detect --help仍能跑:旧命令是隐藏别名,新命令是gitleaks git(扫仓库)和gitleaks dir(扫目录)。输出也一致(同样 5 处命中),但参数形态不同:git子命令的位置参数是仓库路径,不认--source(实测报unknown flag),detect --source .对应的新写法是gitleaks git .;protect不加--staged等于没扫:实测对未暂存的新文件,输出scanned ~0 bytes (0)+no leaks found,不报错也不命中,极易误以为安全。pre-commit 用法必须带--staged;- 退出码 1 = 有泄露,不是程序错误:
detect/protect命中后$?都是 1。在 Git Bash 里连管道测试时要小心,别把「有泄露」当成「命令失败」处理,CI 里则要主动消费这个码; - 自造假 key 别抄文档示例:
AKIAIOSFODNN7EXAMPLE被默认 allowlist 豁免,扫了也不报; - AWS 规则只认 base32 字符集:规则正则是
(?:A3T[A-Z0-9]|AKIA|ASIA|ABIA|ACCA)[A-Z2-7]{16},我第一版假 key 里带了个8,直接不匹配。假 key 也要按真实格式造(只用 A-Z 和 2-7); - 扫描报告自己就是泄露源:
report.json含密钥明文,提交进仓库后被 gitleaks 当成普通文件扫,14 处命中里有 8 处是它对自己报告文件的重复命中,要靠 allowlist 收掉;而且git rm --cached report.json不够,历史提交里的 blob 照样能扫出来,必须重写历史(BFG / git filter-repo)并先轮换密钥; - JSON 字段名是
StartLine:见第三节; - Git Bash 的换行警告会插进输出:
git add时 git 自己打的warning: LF will be replaced by CRLF会和 gitleaks 的日志交错出现,别以为是 gitleaks 的告警。实测 gitleaks 扫 commit 里的 blob(LF),不受工作区 CRLF 影响。
六、适合谁,边界在哪
适合:任何有 git 仓库的团队:pre-commit 卡手滑、CI 卡漏网、开源前自查历史;单二进制零依赖,Windows/macOS/Linux 一个 zip 搞定,接入成本极低。
边界:它做规则匹配,不验证密钥是否真的有效(TruffleHog 有做活性验证的方向,两者互补);222 条规则覆盖常见厂商,内部自研格式要靠自定义规则补;误报治理需要持续维护 allowlist,放太宽等于没开。
未实测(如实标注):pre-commit 框架(.pre-commit-config.yaml)接入、GitHub Actions / SARIF 报告导入、--baseline-path 基线文件、.gitleaksignore 按指纹豁免、以及十万行级以上大仓库的扫描耗时,本次均未验证。
密钥一旦推过远端,正确的处理顺序是:先轮换,再清历史,最后加防护。
参考与署名:本文实测项目为 gitleaks/gitleaks(MIT License,实测版本 v8.30.1,Windows x64,2026-10-14 下载、2026-10-19 重跑核对;星标数 30101 为 GitHub API 当日值);文中配置与测试代码均为本次实跑自写;运行环境 Windows 10 + Git Bash(git 2.55.0.windows.3);所有密钥均为自造假值或本地生成的一次性测试密钥。
- 点赞
- 收藏
- 关注作者
评论(0)