Skip to main content

从存储库中删除敏感数据

如果可以与已克隆存储库的所有人仔细协调,并愿意管理副作用,则可以从存储库的历史记录中移除敏感数据__。

关于从存储库中删除敏感数据

使用 git-filter-repo 之类的工具更改存储库的历史记录时,了解相关影响至关重要。 重写历史记录需要与协作者仔细协调才能成功执行,并且必须管理一些副作用。

请务必注意,如果需要移除的敏感数据为机密(例如密码/令牌/凭据),通常都如此,那么在第一步中,需要撤销和/或轮换该机密。 一旦密钥被撤销或轮换,该密钥不再能够用于访问,这可能足以解决你的问题。 执行额外的步骤来重写历史记录并移除机密可能没有必要。

重写历史记录的副作用

重写历史记录会产生许多副作用;其中包括:

  • 重新污染的风险很高****:不幸的是,很容易将敏感数据重新推送到仓库,从而造成更大的混乱。 如果另一个开发人员拥有你重写之前的一个克隆,并且在你重写之后仅需运行 git pull,然后运行 git push,敏感数据就会返回。 他们需要么丢弃其克隆并重新克隆,或仔细按照多个步骤清理其克隆
  • 丢失其他开发人员工作的风险****:如果其他开发人员在你尝试清理时继续更新包含敏感数据的分支,你将被迫重新清理或放弃他们的工作。
  • 提交哈希发生变更:重写历史记录将更改引入敏感数据的提交的哈希以及之后所有提交的哈希****__。 依赖于提交哈希不变更的任何工具或自动化都将中断或出现问题。
  • 分支保护难题:如果有阻止强制推送的任何分支保护,则必须关闭这些保护(至少暂时关闭)才能移除敏感数据。
  • 已关闭拉取请求的差异视图损坏:移除敏感数据将需要删除用于显示拉取请求差异视图的内部引用,因此你将无法再查看这些差异 这不仅适用于引入敏感数据的 PR,也适用于在敏感数据 PR 合并后基于历史版本生成的任何 PR(即使后续 PR 没有添加或修改任何包含敏感数据的文件)。
  • 与开放拉取请求的交互不佳:更改的提交 SHA 将产生另外的 PR 差异,而旧 PR 差异上的注释可能会失效并丢失,这可能会给作者和审阅者带来困惑。 我们建议在从存储库中删除文件之前合并或关闭所有未完结的拉取请求。
  • 提交和标记上的签名丢失****:提交或标记的签名取决于提交哈希;由于提交哈希由历史记录重写修改,因此签名将不再有效,许多历史记录重写工具(包括 git-filter-repo)将单纯移除签名。 事实上,git-filter-repo 还将移除在移除敏感数据之前的提交的签名和标签签名。 (技术上,可使用 --refs 选项配合 git-filter-repo 进行规避,但需小心确保你指定了历史中包含敏感数据的所有 refs 以及引入敏感数据的提交)
  • 将其他人直接引导至敏感数据****:Git 在设计时已在提交标识符中内置了加密检查,这样不法之徒就无法侵入服务器并在不受注意的情况下修改历史记录。 从安全角度来看,这很有帮助,但从敏感数据的角度来看,这意味着删除敏感数据是一个颇为复杂的协调过程;这进一步意味着,在修改历史记录时,拥有现有克隆的线索用户将注意到历史记录差异,并利用这一点快速轻松地找到你从中央存储库中移除但克隆中仍然存在的敏感数据。

关于敏感数据泄露

从存储库中移除敏感数据涉及四个大致步骤:

  • 使用 git-filter-repo 在本地重写存储库
  • 使用本地重写历史记录更新GitHub上的存储库
  • 与同事协调,以清理存在的其他克隆
  • 防止重复并避免将来的敏感数据溢出

如果仅重写历史并强制推送,包含敏感数据的提交可能仍可在其他地方访问:

  • 在存储库的任何克隆或分支中
  • 直接通过 GitHub 上缓存视图中的 SHA-1 哈希
  • 通过引用它们的任何 pull request

你无法从其他用户的仓库克隆中移除敏感数据;你需要将 手册中“确保其他副本已清理:同事的克隆”中的说明发送给他们,让他们自行操作git-filter-repo 但是,你可以通过联系 GitHub 永久移除 通过网站管理员GitHub支持门户 上拉取请求中敏感数据的缓存视图和引用。

重要

GitHub 支持 不会删除非敏感数据,且仅会在我们认定无法通过轮换受影响的凭据来降低风险的情况下,协助删除敏感数据。

如果引入敏感数据的提交存在于任何分支中,则该提交将继续在那里访问。 你需要与分支的所有者协调,要求他们删除敏感数据或完全删除分支。

GitHub 无法为这些所有者提供联系信息。

在决定重写存储库的历史记录时,应考虑这些限制和挑战。

使用 git-filter-repo 从本地仓库的历史记录中清除文件

  1. 安装 git-filter-repo 工具的最新版本。 你需要一个带有 --sensitive-data-removal 标志的版本,这意味着至少是版本 2.47。 可以手动安装 git-filter-repo,也可以使用包管理器安装。 例如,若使用 HomeBrew 安装工具,请使用 brew install 命令。

    brew install git-filter-repo
    

    有关详细信息,请参阅 INSTALL.md 文件,位于 存储库 中。

  2. 将仓库克隆到本地计算机。 请参阅“克隆仓库”。

    git clone https://github.com/YOUR-USERNAME/YOUR-REPOSITORY
    
  3. 导航到存储库的工作目录。

    cd YOUR-REPOSITORY
    
  4. 运行 git-filter-repo 命令以清理敏感数据。

    如果希望从所有分支/标签/引用中删除特定文件,请运行以下命令,将 PATH-TO-YOUR-FILE-WITH-SENSITIVE-DATA 替换为要删除的文件的 git 路径,而不仅仅是文件名(例如 ****)src/module/phone-numbers.txt

    git-filter-repo --sensitive-data-removal --invert-paths --path PATH-TO-YOUR-FILE-WITH-SENSITIVE-DATA
    

    重要

    如果包含敏感数据的文件曾经存在于任何其他路径(因为它被移动或重命名过),你必须为该文件添加一个额外的 --path 参数,或者再次运行该命令并指定替代路径。

    如果你希望替换仓库历史记录中所有非二进制文件中列出的 ../passwords.txt 文本,可以运行以下命令:

    git-filter-repo --sensitive-data-removal --replace-text ../passwords.txt
    
  5. 仔细检查是否已从存储库的历史记录中移除所需的所有内容。

  6. 找出有多少个拉取请求会受到此次历史记录重写的影响。 后文需要用到此信息。

    $ grep -c '^refs/pull/.*/head$' .git/filter-repo/changed-refs
    4
    

    可以删除 -c 以查看受影响的拉取请求:

    $ grep '^refs/pull/.*/head$' .git/filter-repo/changed-refs
    refs/pull/589/head
    refs/pull/602/head
    refs/pull/604/head
    refs/pull/605/head
    

    此输出在第二和第三个斜杠之间包含拉取请求编号 如果受影响的拉取请求数大于预期,则可以放弃此克隆,且不会造成不良影响,然后重新进行重写或放弃敏感数据的删除。 进入下一步后,重写将变得不可逆。

  7. 一旦你对存储库的状态满意,强制推送你的本地更改以覆盖 GitHub.com 上的存储库。 尽管 --force 已由 --mirror 隐含,但我们在下方提醒你,这会强制更新所有分支、标签和 refs,并丢弃在清理仓库期间其他人可能对这些 refs 的修改

    git push --force --mirror origin
    

    此命令将无法推送任何以 refs/pull/ 开头的 refs,因为 GitHub 将这些 refs 标记为只读。 下一部分将处理这些推送失败。 如果其他 refs 推送失败,很可能该分支启用了分支保护,需要暂时关闭并重新推送 重复操作,直到唯一未能更新的 refs 为 refs/pull/ 开头的

从GitHub中完全删除数据

在使用 git-filter-repo 删除敏感数据并将更改推送到 GitHub 之后,还必须执行几个额外步骤,才能将这些数据从 GitHub 中彻底删除。

  1. 联系 通过网站管理员GitHub支持门户,并提供以下信息:

    • 相关所有者和仓库名称(例如 YOUR-USERNAME/YOUR-REPOSITORY)。

    • 在上一步中找到的受影响的拉取请求数。 支持部门将使用此信息来验证你是否了解将会受到影响的范围。

    • git-filter-repo 报告的“First Changed Commit”(在其输出中查找 NOTE: First Changed Commit(s)

    • 如果 NOTE: There were LFS Objects Orphaned by this rewrite 出现在 git-filter-repo 的输出中(紧跟在“First Changed Commit”后),请提到你有 LFS 对象被孤立”,并将命名的文件也上传到工单中。

如果你已成功清理除 PR 之外的所有引用,且没有派生包含对敏感数据的引用,支持团队将随后:

* 在 GitHub 上取消引用或删除任何受影响的 PR。
* 在服务器上运行垃圾回收,以从存储中删除敏感数据。
* 删除缓存的视图。
* 如果涉及 LFS 对象,请删除和/或清除孤立的 LFS 对象。



 > [!IMPORTANT] 

GitHub 支持 不会删除非敏感数据,并且仅在我们确定无法通过轮换受影响的凭据来缓解风险的情况下,才有助于删除敏感数据。

  1. 协作者必须对从旧(受污染)仓库历史创建的任何分支执行 rebase而非 merge 一次合并提交可能会重新引入您刚刚遇到清除问题的部分或全部污染的历史记录。 他们可能还需要采取额外步骤;请参阅 手册“确保其他副本已清理:同事的克隆”git-filter-repo

避免将来的意外提交

防止参与者进行意外提交有助于防止敏感信息泄露。 有关详细信息,请参阅“防止组织中数据泄露的最佳做法”。

可以执行一些操作来避免提交或推送不应共享的内容:

  • 如果在不应由 git 跟踪的文件中发现敏感数据,请将该文件名添加到 .gitignore(并确保提交该更改并将其推送到 .gitignore,这样其他开发人员就能受到保护)。
  • 避免在代码中对机密进行硬编码。 使用环境变量或机密管理服务(如 Azure 密钥保管库、AWS 机密管理器或 HashiCorp Vault)在运行时管理和注入机密。
  • 创建预提交挂钩,以便在将敏感数据提交或推送到任何位置之前对其进行检查,也可以在预提交挂钩中使用已知工具,如 git-secrets 或 gitleaks。 (确保每位协作者都设置了你选择的 pre-commit hook。)
  • 使用像 GitHub Desktopgitk 这样的可视化程序来提交更改。 可视程序通常可以更容易地查看每次提交时将添加、删除和修改具体哪些文件。
  • 避免在命令行中使用笼统的命令 git add .git commit -a,而是使用 git add filenamegit rm filename 来单独暂存文件。
  • 使用 git add --interactive 单独查看和暂存每个文件中的更改。
  • 使用 git diff --cached 查看为提交暂存的更改。 如果不使用 git commit 标志,这就是 -a 将产生的确切差异。
  • 为存储库启用推送保护,以检测包含硬编码机密的推送并防止将其提交到代码库。 有关详细信息,请参阅“推送保护”。

其他阅读材料