从Git历史记录中删除敏感文件及其提交

我想把一个Git项目放在GitHub上,但它包含某些具有敏感数据的文件(用户名和密码,如/config/deploy。卡皮斯特拉诺的Rb)。

我知道我可以将这些文件名添加到.gitignore,但这不会删除它们在Git中的历史记录。

我也不想通过删除/来重新开始。git目录。

有没有办法在你的Git历史中删除一个特定文件的所有痕迹?

195694 次浏览

从你的问题中不清楚你的git存储库是否完全是本地的,或者你是否在其他地方有一个远程存储库;如果它是远程的,而且不安全,你就有问题了。如果有人在您修复此问题之前克隆了该存储库,他们将在其本地机器上拥有您的密码副本,并且您无法强迫他们更新到您的“固定”版本,因为它已经从历史记录中消失了。你能做的唯一安全的事情就是在你使用过密码的地方将密码更改为其他密码。


有了这些,下面是如何解决它的方法。GitHub以FAQ的形式回答了这个问题:

Windows用户注意事项:在此命令中使用双引号(")而不是单引号

git filter-branch --index-filter \
'git update-index --remove PATH-TO-YOUR-FILE-WITH-SENSITIVE-DATA' <introduction-revision-sha1>..HEAD
git push --force --verbose --dry-run
git push --force

2019年更新:

这是FAQ中的当前代码:

  git filter-branch --force --index-filter \
"git rm --cached --ignore-unmatch PATH-TO-YOUR-FILE-WITH-SENSITIVE-DATA" \
--prune-empty --tag-name-filter cat -- --all
git push --force --verbose --dry-run
git push --force

请记住,一旦您将这段代码推到GitHub等远程存储库,其他人已经克隆了该远程存储库,您现在就处于重写历史的情况下。在此之后,当其他人尝试下拉您的最新更改时,他们将收到一条消息,指示无法应用更改,因为它不是快进。

为了解决这个问题,他们必须删除现有的存储库并重新克隆它,或者遵循git-rebase从中“从UPSTREAM REBASE中恢复”下的说明。

提示:执行git rebase --interactive


在将来,如果你不小心提交了一些敏感信息的更改,但你注意到之前被推送到远程存储库,有一些更容易的修复方法。如果上次提交是添加敏感信息的提交,您可以简单地删除敏感信息,然后运行:

git commit -a --amend

这将用你所做的任何新更改来修改之前的提交,包括用git rm完成的整个文件移除。如果更改在历史上更早,但仍然没有推送到远程存储库,您可以进行交互式rebase:

git rebase -i origin/master

这将打开一个编辑器,其中包含自与远程存储库的最后一个共同祖先以来所做的提交。在表示提交敏感信息的任何行上,将“pick”更改为“edit”,然后保存并退出。Git将遍历这些更改,并将您留在以下位置:

$EDITOR file-to-fix
git commit -a --amend
git rebase --continue

对于每个具有敏感信息的更改。最终,您将回到您的分支,并且您可以安全地推动新的更改。

我推荐David Underhill的这个脚本,对我来说很有魅力。

它在natacado的filter分支中添加了这些命令来清理它留下的混乱:

rm -rf .git/refs/original/
git reflog expire --all
git gc --aggressive --prune

完整剧本(全部由大卫·安德希尔提供)

#!/bin/bash
set -o errexit


# Author: David Underhill
# Script to permanently delete files/folders from your git repository.  To use
# it, cd to your repository's root and then run the script with a list of paths
# you want to delete, e.g., git-delete-history path1 path2


if [ $# -eq 0 ]; then
exit 0
fi


# make sure we're at the root of git repo
if [ ! -d .git ]; then
echo "Error: must run this script from the root of a git repository"
exit 1
fi


# remove all paths passed as arguments from the history of the repo
files=$@
git filter-branch --index-filter \
"git rm -rf --cached --ignore-unmatch $files" HEAD


# remove the temporary history git-filter-branch
# otherwise leaves behind for a long time
rm -rf .git/refs/original/ && \
git reflog expire --all && \
git gc --aggressive --prune

最后两个命令如果改为下面的命令可能会更好:

git reflog expire --expire=now --all && \
git gc --aggressive --prune=now

更改密码是一个好主意,但对于从回购历史中删除密码的过程,我推荐高炉煤气Repo-Cleaner,这是一个更快、更简单的替代git-filter-branch,专门用于从Git回购中删除私人数据。

创建一个private.txt文件,列出你想要删除的密码等(每行一个条目),然后运行以下命令:

$ java -jar bfg.jar  --replace-text private.txt  my-repo.git

你的回购历史记录中小于阈值大小(默认为1MB)的所有文件将被扫描,任何匹配的字符串(不在你的最新的提交中)将被替换为字符串“***REMOVED***”。然后你可以使用git gc来清除死数据:

$ git gc --prune=now --aggressive

BFG通常比运行git-filter-branch快10-50倍,选项是围绕以下两个常见用例进行简化和定制的:

  • 删除疯狂的大文件
  • 删除密码、凭证 &其他私人数据

完全披露:我是好心眼巨人回收清理器的作者。

它看起来是这样的:

git rm --cached /config/deploy.rb
echo /config/deploy.rb >> .gitignore

从git中删除跟踪文件的缓存,并将该文件添加到.gitignore列表中

明确一点:公认的答案是正确的。先试试。然而,对于某些用例来说,这可能是不必要的复杂,特别是当你遇到诸如'fatal: bad revision -prune-empty'之类的讨厌错误时,或者真的不关心你的回购历史。

另一种选择是:

  1. CD到项目的基本分支
  2. 删除敏感代码/文件
  3. rm -rf .git/ #删除所有git信息 你的代码
  4. 去github并删除你的存储库
  5. 按照本指南将您的代码推送到一个新的存储库,就像您通常会做的那样- 李https://help.github.com/articles/adding-an-existing-project-to-github-using-the-command-line/ < / >

当然,这将删除所有提交历史分支,以及来自你的github回购和本地git回购的问题。如果这是不可接受的,你将不得不使用另一种方法。

我们可以称之为“核选项”。

如果您推送到GitHub,强制推送不够,请删除存储库或联系技术支持

即使你在一秒钟后强行推,这也不够,如下所述。

唯一有效的做法是:

  • 是什么泄露了一个像密码一样可更改的凭证?

    • yes:立即修改您的密码,并考虑使用更多的OAuth和API密钥!

    • no(裸照):

      • 你关心存储库中的所有问题得到nuked吗?

        • no:删除存储库

        • < p >是的:

          • 联络支持
          • 如果泄漏对你来说非常重要,以至于你愿意获得一些存储库停机时间,以使它不太可能泄漏,让它私有,而你等待GitHub支持回复你

一秒钟后的推力是不够的,因为:

如果你删除存储库,而不是强制推送,提交甚至会立即从API中消失,并给出404,例如https://api.github.com/repos/cirosantilli/test-dangling-delete/commits/8c08448b5fbf0f891696819f3b2b2d653f7a3824,即使你用相同的名称重新创建另一个存储库,这也是有效的。

为了测试这一点,我创建了一个repo: https://github.com/cirosantilli/test-dangling,并执行:

git init
git remote add origin git@github.com:cirosantilli/test-dangling.git


touch a
git add .
git commit -m 0
git push


touch b
git add .
git commit -m 1
git push


touch c
git rm b
git add .
git commit --amend --no-edit
git push -f

参见:如何从GitHub删除悬空提交?

现在正式推荐使用git filter-repo而不是git filter-branch

这在Git 2.5本身的git filter-branch的手册页中提到过。

使用git过滤器repo,你可以用:从git/GitHub's历史中删除文件夹及其内容删除某些文件

pip install git-filter-repo
git filter-repo --path path/to/remove1 --path path/to/remove2 --invert-paths

这将自动删除空提交。

或者你可以用:如何在整个Git历史中替换字符串?替换某些字符串

git filter-repo --replace-text <(echo 'my_password==>xxxxxxxx')

这是我在windows下的解决方案

git filter-branch——tree-filter "rm -f 'filedir/filename'

Git push—force

确保路径正确 否则它将不起作用

我希望这对你们有帮助

你可以使用git forget-blob

git forget-blob file-to-forget的用法非常简单。你可以在这里获得更多信息

https://ownyourbits.com/2017/01/18/completely-remove-a-file-from-a-git-repository-with-git-forget-blob/

它将从历史记录、reflog、标签等所有提交中消失

我时不时地会遇到同样的问题,每次我都要回到这个帖子和其他帖子,这就是为什么我把这个过程自动化了。

感谢Stack Overflow的贡献者,是他们让我把这些放在一起

到目前为止,我已经做过几次了。注意,这一次只对一个文件有效。

  1. 获取修改文件的所有提交的列表。底部的将是第一个提交:

    git log --pretty=oneline --branches -- pathToFile < / p >

  2. 要从历史记录中删除文件,使用第一个提交sha1和之前命令中的文件路径,并将它们填充到这个命令中:

    git filter-branch --index-filter 'git rm --cached --ignore-unmatch <path-to-file>' -- <sha1-where-the-file-was-first-added>.. < / p >

使用filter-branch:

git filter-branch --force --index-filter 'git rm --cached --ignore-unmatch *file_path_relative_to_git_repo*' --prune-empty --tag-name-filter cat -- --all


git push origin *branch_name* -f

在我的android项目中,我在应用程序/ / src / main / res /值文件夹中有admob_keys.xml作为分离的xml文件。要删除这个敏感的文件,我使用下面的脚本和工作完美。

git filter-branch --force --index-filter \
'git rm --cached --ignore-unmatch  app/src/main/res/values/admob_keys.xml' \
--prune-empty --tag-name-filter cat -- --all

考虑到OP正在使用GitHub,如果将敏感数据提交到Git repo中,可以使用前面的选项之一将其完全从历史记录中删除(下面阅读更多关于它们的信息):

  1. git filter-repo工具(在GitHub上查看源代码)。

  2. 高炉煤气Repo-Cleaner工具(它是开源的- 在GitHub上查看源代码)。

在前面的一个选项之后,还需要执行其他步骤。检查下面的额外的部分。

如果目标是删除在最近的未推送提交中添加的文件,请阅读下面的替代部分。

为了将来的考虑,为了防止类似的情况,检查下面的为了未来部分。


选项1

使用git filter-repo。在继续之前,请注意这一点

如果你在存储更改后运行git filter-repo,你将不能用其他存储命令检索您的更改。在运行git filter-repo之前,我们建议卸载你所做的任何更改。要取消您所存储的最后一组更改,请运行git stash show -p | git apply -R。有关更多信息,请参见Git工具-存储和清洗

现在让我们从一个回购的历史记录中删除一个文件,并将其添加到.gitignore中(以防止再次提交)。

在继续之前,确保其中一个已经安装了git filter-repo (阅读这里如何安装它),并且其中一个已经安装了一个repo的本地副本(如果不是这样,则请参阅这里如何克隆存储库)。

  1. 打开git并访问存储库。

    cd YOUR-REPOSITORY
    
  2. (可选)备份.git/config文件。

  3. < p >运行

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

    PATH-TO-YOUR-FILE-WITH-SENSITIVE-DATA替换为要删除的文件的路径,而不仅仅是其文件名:

    • 强制Git处理,但不检查每个分支和标记的整个历史。

    • 删除指定的文件(以及由此产生的空提交)

    • 删除一些配置(例如存储在.git/config文件中的远程URL)

    • < p > 覆盖现有的标记

  4. 将敏感数据文件添加到.gitignore

    echo "YOUR-FILE-WITH-SENSITIVE-DATA" >> .gitignore
    
    
    git add .gitignore
    
    
    git commit -m "Add YOUR-FILE-WITH-SENSITIVE-DATA to .gitignore"
    
  5. 检查是否从存储库历史中删除了所有内容,并且所有分支都已签出。然后再进行下一步。

  6. 强制推送本地更改以覆盖你在GitHub.com上的存储库,以及你已经推送的所有分支。强制推送需要从提交历史记录中删除敏感数据。阅读这个答案底部的第一个注释,了解更多细节。

    git push origin --force --all
    

选项2

使用高炉煤气Repo-Cleaner。这比git filter-branch更快更简单。

例如,要删除包含敏感数据的文件并保持最新提交不变,请运行

bfg --delete-files YOUR-FILE-WITH-SENSITIVE-DATA

要替换passwords.txt中列出的所有文本,只要它可以在存储库的历史记录中找到,请运行

bfg --replace-text passwords.txt

在删除敏感数据后,必须强制将更改推送到GitHub。

git push --force

额外的

使用上述选项之一后:

  1. < p > 联系GitHub支持

  2. (如果与团队一起工作)告诉他们变基,而不是合并,他们从旧的(受污染的)存储库历史中创建的任何分支。一次合并提交可能会重新引入一些或全部刚刚清理时遇到的问题历史。

  3. 过了一段时间,并且确信没有意外的副作用后,可以使用以下命令强制解除引用本地存储库中的所有对象并进行垃圾收集(使用Git 1.8.5或更新版本):

    git for-each-ref --format="delete %(refname)" refs/original | git update-ref --stdin
    
    
    git reflog expire --expire=now --all
    
    
    git gc --prune=now
    

替代

如果该文件是在最近的提交中添加的,并且没有推送到GitHub.com,则可以删除该文件并修改提交:

  1. 打开git并访问存储库。

    cd YOUR-REPOSITORY.l
    
  2. 要删除文件,输入git rm --cached:

    git rm --cached GIANT_FILE
    # Stage our giant file for removal, but leave it on disk
    
  3. 使用--amend -CHEAD提交此更改:

    git commit --amend -CHEAD
    # Amend the previous commit with your change
    # Simply making a new commit won't work, as you need
    # to remove the file from the unpushed history as well
    
  4. 推送一个人的提交到GitHub.com:

    git push
    # Push our rewritten, smaller commit
    

为了未来

为防止敏感资料外泄,其他良好做法包括:

  • 使用可视化程序提交更改。有各种替代方案(如GitHub桌面GitKrakengitk,…),可以更容易地跟踪变化。

  • 避免使用catch-all命令git add .git commit -a。相反,可以使用git add filenamegit rm filename来单独分段文件。

  • 使用git add --interactive单独检查和阶段每个文件中的变化。

  • 使用git diff --cached来检查已经提交的更改。只要不使用-a标志,这就是git commit将产生的确切差异。

  • 在安全硬件中生成密钥(HSM盒子,硬件密钥-如Yubikey / Solokey),永远不会离开它。

  • x508上训练团队。


注:

  • 当一个强制执行时,它将重写存储库历史,从而从提交历史中删除敏感数据。这可能会覆盖其他人基于其工作的提交。

  • 对于这个答案,一个人使用了一些GitHub帖子的内容: