12k
All articles

当你的 API 密钥泄露到代码仓库时该怎么办

详解如何撤销已泄露的 API 密钥,清理 Git 历史记录,并借助推送保护与 secret scanning 防止密钥流入 GitHub 仓库。

OpenReplay Team
OpenReplay Team
当你的 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-repoBFG Repo-Cleaner 等工具可以提供帮助。但要明白,如果你的仓库公开了任何时间,就要假设密钥已被捕获。

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 路由、无服务器函数或后端代理。你的前端对用户进行身份验证;你的后端持有密钥。

启用推送保护。 这会在错误变成事故之前捕获它们。

使用预提交钩子。gitleaksdetect-secrets 这样的工具可以在本地扫描暂存的更改。

添加 CI 扫描。 在你的流水线中运行密钥检测作为安全网。

严格限制密钥。 即使是公共客户端密钥也应该按域名、IP 或 API 范围进行限制。

结论

当 API 密钥泄露时,速度比完美更重要。先撤销,立即轮换,然后将清理历史记录作为次要步骤。不要混淆公共客户端密钥和真正的密钥,永远不要相信环境变量能在前端构建中隐藏任何东西。

今天就启用 GitHub 的推送保护。这是确保这个问题不再发生的最简单方法。

常见问题

当我发现 API 密钥泄露时应该首先做什么?

通过你的 API 提供商的控制面板立即撤销或禁用已泄露的密钥。不要等到先清理 Git 历史记录。密钥在推送到公共仓库的那一刻就已经被泄露了,自动化机器人可以在几秒钟内捕获它。撤销后,生成新密钥并更新你的应用程序。

我可以只删除包含密钥的提交来解决问题吗?

不可以。Git 保留所有历史记录,因此密钥仍然可以在先前的提交、分支副本、现有克隆以及修复前创建的任何副本中访问。删除或修改提交只会将其从当前分支顶端删除。无论你之后执行什么历史清理,都必须撤销密钥。

像 NEXT_PUBLIC 或 REACT_APP 这样的环境变量存储 API 密钥安全吗?

不安全。这些带前缀的环境变量会直接打包到你的前端 JavaScript 中,任何打开浏览器开发者工具的人都能看到。它们用于公共配置值,而不是密钥。任何授予特权访问的密钥都必须存储在服务器端,并通过 API 路由或后端代理访问。

我如何防止将来意外提交密钥?

启用 GitHub 推送保护,在包含检测到的密钥的提交到达仓库之前将其拦截。使用带有 gitleaks 或 detect-secrets 等工具的预提交钩子在本地扫描更改。在 CI 流水线中添加密钥检测作为额外的安全网。这些层级协同工作,尽早捕获错误。

Understand every bug

Uncover frustrations, understand bugs and fix slowdowns like never before with OpenReplay — self-hosted, with full data ownership.

Star on GitHub

We use cookies to improve your experience. By using our site, you accept cookies.