当你的 API 密钥泄露到代码仓库时该怎么办
你把代码推送到 GitHub,几秒钟后才意识到 API 密钥也一起推上去了。也许 GitHub 给你发了警报,也许 GitGuardian 给你发了邮件。无论如何,你现在正在处理前端应用中的密钥泄露问题——而且时间紧迫。
大多数开发者容易犯的关键错误是:从最新提交中删除密钥并不能解决问题。Git 历史记录会保留所有内容。可能已经存在分支副本。自动化机器人会在推送后的几秒钟内扫描公共仓库。
本文将详细介绍当 API 密钥在 GitHub 上泄露时该怎么做,如何区分真正敏感的密钥和公共客户端密钥,以及如何防止再次发生这种情况。
核心要点
- 立即撤销和轮换已泄露的密钥——清理 Git 历史是次要的,因为密钥已经被泄露了。
- 区分公共客户端密钥(设计用于浏览器使用)和真正的密钥(需要紧急处理的特权凭证)。
- Git 历史记录保留所有内容,因此删除提交并不能从分支副本、缓存或现有克隆中删除密钥。
- 启用 GitHub 的推送保护功能,在包含密钥的提交到达仓库之前将其拦截。
- 永远不要在前端代码中存储特权密钥——使用服务器端 API 路由或后端代理。
首先,了解你实际泄露了什么
并非所有密钥都具有相同的风险。在你恐慌之前,先识别泄露的是哪种类型的凭证。
公共客户端密钥设计为可见的。限制在特定域名的 Google Maps API 密钥、分析令牌,或明确标记为客户端使用的密钥(如 Next.js 中的 NEXT_PUBLIC_ 或 Vite 中的 VITE_)都是要打包到浏览器包中的。这些仍然应该有使用限制,但泄露并不是灾难性的。
真正的密钥授予特权访问:Stripe 密钥、AWS 凭证、数据库连接字符串,或任何可以读写敏感数据或产生费用的密钥。这些需要立即采取行动。
这种区分很重要,因为你的响应应该与严重程度相匹配。
即时响应:先撤销和轮换
当你泄露了真正的密钥时,清理历史记录是次要的。你的首要任务是轮换已泄露的 API 密钥。
步骤 1:立即撤销已泄露的密钥。 登录到你的 API 提供商的控制面板,删除或禁用已泄露的凭证。不要等到清理完 Git 历史记录——密钥已经被泄露了。
步骤 2:生成具有最小权限范围的新密钥。 应用最小权限原则。尽可能按 IP、域名或特定 API 端点进行限制。
步骤 3:更新你的应用程序。 在应用程序崩溃之前将新密钥部署到你的环境中。
一些云服务提供商会自动禁用他们在公共仓库中检测到的凭证。例如,Google Cloud 默认可以自动禁用已泄露的服务账号密钥。其他提供商,包括 AWS,通常会通知你,但不会始终自动撤销密钥。所有这些提供商都参与了 GitHub 的密钥扫描合作伙伴计划。不要依赖自动化——无论如何都要立即采取行动。
为什么删除提交还不够
Git 永远不会忘记。即使你从当前分支中删除了密钥,它仍然存在于:
- 通过
git log可访问的先前提交 - 在你修复之前创建的分支副本
- 其他用户或机器人拉取的现有克隆
- 删除前创建的任何镜像
这就是为什么撤销是第一位的。清理历史记录是遏制,而不是补救。
如果你需要清理历史记录,git filter-repo 或 BFG Repo-Cleaner 等工具可以提供帮助。但要明白,如果你的仓库公开了任何时间,就要假设密钥已被捕获。
Discover how at OpenReplay.com.
GitHub 密钥扫描和推送保护
GitHub 为这个问题提供了一流的保护。密钥扫描和推送保护适用于所有公共仓库,以及通过 GitHub Secret Protection(也包含在 GitHub Advanced Security 中)的私有仓库。
密钥扫描检测仓库中已知的凭证模式,并自动向你和/或提供商发出警报。
推送保护更进一步——它在包含检测到的密钥的提交到达仓库之前就将其拦截。这是目前最有效的预防机制。
在仓库设置中的 Settings → Code security and analysis 下启用这两项功能,或查看关于 GitHub Secret Protection 的官方文档。
前端环境变量陷阱
这是一个困扰许多 React、Vue 和 Next.js 开发者的错误:为客户端使用而添加前缀的环境变量不是密钥。
像 REACT_APP_*、NEXT_PUBLIC_* 或 VITE_* 这样的变量会直接打包到你的 JavaScript 中。任何人都可以打开开发者工具找到它们。这些不是隐藏的——它们只是被组织起来了。
如果一个密钥授予特权访问,它就不能存在于前端代码中。就是这样。
前端团队的预防策略
将特权密钥保留在服务器端。 使用 API 路由、无服务器函数或后端代理。你的前端对用户进行身份验证;你的后端持有密钥。
启用推送保护。 这会在错误变成事故之前捕获它们。
使用预提交钩子。 像 gitleaks 或 detect-secrets 这样的工具可以在本地扫描暂存的更改。
添加 CI 扫描。 在你的流水线中运行密钥检测作为安全网。
严格限制密钥。 即使是公共客户端密钥也应该按域名、IP 或 API 范围进行限制。
结论
当 API 密钥泄露时,速度比完美更重要。先撤销,立即轮换,然后将清理历史记录作为次要步骤。不要混淆公共客户端密钥和真正的密钥,永远不要相信环境变量能在前端构建中隐藏任何东西。
今天就启用 GitHub 的推送保护。这是确保这个问题不再发生的最简单方法。
常见问题
通过你的 API 提供商的控制面板立即撤销或禁用已泄露的密钥。不要等到先清理 Git 历史记录。密钥在推送到公共仓库的那一刻就已经被泄露了,自动化机器人可以在几秒钟内捕获它。撤销后,生成新密钥并更新你的应用程序。
不可以。Git 保留所有历史记录,因此密钥仍然可以在先前的提交、分支副本、现有克隆以及修复前创建的任何副本中访问。删除或修改提交只会将其从当前分支顶端删除。无论你之后执行什么历史清理,都必须撤销密钥。
不安全。这些带前缀的环境变量会直接打包到你的前端 JavaScript 中,任何打开浏览器开发者工具的人都能看到。它们用于公共配置值,而不是密钥。任何授予特权访问的密钥都必须存储在服务器端,并通过 API 路由或后端代理访问。
启用 GitHub 推送保护,在包含检测到的密钥的提交到达仓库之前将其拦截。使用带有 gitleaks 或 detect-secrets 等工具的预提交钩子在本地扫描更改。在 CI 流水线中添加密钥检测作为额外的安全网。这些层级协同工作,尽早捕获错误。
Understand every bug
Uncover frustrations, understand bugs and fix slowdowns like never before with OpenReplay — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data. Check our GitHub repo and join the thousands of developers in our community.