一句话版本
把 API 密钥、数据库密码、私钥提交进代码仓库,是最常见、也最容易避免的安全事故之一。一旦推送到公开仓库,自动化的扫描机器人往往几分钟内就会发现并开始利用。更麻烦的是,Git 会记住一切:删掉文件再提交,密钥仍然躺在历史记录里。 对策分三道:提交前用钩子拦截,推送时由平台拦截,事后持续扫描。而一旦泄露,唯一可靠的补救是立即轮换密钥。
全文分三层:
- 通俗理解:把钥匙忘在了公共留言板上
- 实际用处:Uber、Toyota 事件,GitHub 推送保护
- 原理与工程要点:怎么识别密钥、三道防线、泄露后的处理流程
第一层:通俗理解
1.1 钥匙忘在了公共留言板上
你把家门钥匙的照片贴在了小区的公共留言板上,几分钟后发现了,赶紧撕掉。
但是:
- 已经有人拍了照;
- 留言板有"历史记录"功能,任何人都能翻到你撕掉之前的版本。
这时唯一的办法是换锁。公开 Git 仓库就是这样的留言板。
1.2 为什么总有人犯
- 调试时临时写进代码,忘了删;
- 把
.env文件提交了(没加进.gitignore); - 把私有仓库改成了公开,没意识到历史里有密钥;
- AI 编程工具为了让代码跑通,把密钥硬编码进去(见《凭证泄露与上下文窗口》)。
第二层:实际用处
- Uber(2016):攻击者进入 Uber 工程师使用的私有代码仓库,在其中找到了 AWS 访问密钥,进而从云存储中下载了约 5700 万用户和司机的数据。Uber 当时还向攻击者支付了 10 万美元以隐瞒此事,后来受到监管处罚,前首席安全官也被起诉。
- Toyota(2022):一个承包商把含有访问密钥的源代码误发布到公开仓库,近五年后才被发现,可能涉及约 30 万条客户邮箱记录。
- 自动化利用:多项安全研究在公开仓库中故意放入"诱饵"云密钥,观察到它们通常在几分钟到几小时内就被尝试使用。
- GitHub 推送保护:GitHub 与大量服务商合作,识别数百种密钥格式;从 2024 年起,公开仓库默认开启推送保护,推送中如果包含已知格式的密钥,会被直接阻止。很多服务商在收到 GitHub 的泄露通知后,会自动作废这把密钥。
第三层:原理与工程要点
3.1 怎么识别密钥
| 方法 | 说明 |
|---|---|
| 固定格式 | 很多服务的密钥有固定前缀和长度,如 AWS 访问密钥以 AKIA 开头、GitHub 令牌以 ghp_ 等开头、私钥文件以 -----BEGIN ... PRIVATE KEY----- 开头。准确率高 |
| 高熵字符串 | 一段看起来很随机的长字符串可能是密钥。误报较多 |
| 上下文 | 变量名叫 password、secret、token 的赋值 |
| 验证 | 部分工具会尝试用发现的密钥调用服务商接口,确认它是否仍然有效,帮助排定处理优先级 |
服务商设计带前缀的密钥格式,本身就是为了让扫描更准确。
3.2 三道防线
| 防线 | 工具 | 作用 |
|---|---|---|
| 提交前 | gitleaks、detect-secrets 等作为 pre-commit 钩子 | 在本地就拦住,密钥从未进入历史 |
| 推送时 | 代码托管平台的推送保护 | 即使本地没装钩子也能拦住 |
| 事后 | 平台的秘密扫描、trufflehog 等扫描全部历史 | 发现已经存在的泄露 |
再加上源头措施:
.gitignore里包含.env、密钥文件、云凭证目录;- 本地开发用钥匙串或密钥管理工具,而不是明文文件(见《最小权限与密钥管理》);
- 前端代码里绝不放任何需要保密的密钥,前端代码对所有人可见。
3.3 泄露之后的处理流程
要点:
- 轮换是第一步,也是唯一可靠的一步。删除提交、改写历史、把仓库设为私有都不够:密钥可能已经被复制,仓库的分叉、缓存、镜像里也还有;
- 改写 Git 历史(如
git filter-repo)可以减少日后被再次发现的机会,但前提是已经轮换; - 检查服务商的访问日志,看泄露窗口内是否有异常调用;
- 复盘并补上缺失的防线(见《事故响应与复盘》)。
3.4 减小泄露的影响
即使做得再好,泄露也可能发生。提前做好这些,能让泄露的影响小很多:
- 密钥权限最小、有效期短(见《最小权限与密钥管理》);
- 云服务设置用量上限和预算告警(见《限流与抗滥用》);
- 尽量用 OIDC 等免密钥方案代替长期密钥(见《CI CD流水线安全》)。
总结
| 层次 | 要点 |
|---|---|
| 风险 | 公开仓库里的密钥几分钟内就可能被利用;Git 历史记住一切 |
| 识别 | 固定前缀格式最准;高熵和上下文作为补充 |
| 防线 | 提交前钩子、推送保护、事后扫描 |
| 泄露后 | 立即轮换;删除记录不够;查日志;复盘 |