我不小心提交了包含敏感数据的文件。我需要通过删除敏感数据来更新该文件,并确保旧版本不会出现在历史记录中。
我知道在本地克隆了回购协议的人仍然可以访问它。但是,一旦他们获取了最新信息,是否可以通过某种方式进行设置,使他们不会看到敏感数据前进或无法在日志中看到它们?
答案 0 :(得分:3)
您可以从历史记录中删除敏感数据。如您所述,任何提取了当前历史记录的现有克隆仍将具有该文件。这些存储库必须进行“修复”才能与远程服务器一起使用(请参阅“从上游资源恢复中”下的git rebase
文档-https://git-scm.com/docs/git-rebase-)。即使在修复之后,这些存储库的用户仍可以根据需要获取数据。 (实际上,即使您确实进行了某种修复过程,也可能会强行从其克隆中删除数据,也不会阻止他们在修复之前复制该数据。)
考虑到这一点,您实际上只需要将该数据视为已泄露。例如,如果它是密码,请更改密码。
并且牢记那,重写历史记录可能不值得。如果敏感数据的类型无法更改,而您所能做的就是减轻现有的泄漏并防止泄漏进一步扩散,那么历史记录编辑的价值在于可以保持 new 从进一步公开数据中克隆出来。但是,如果是密码,那么更改密码将使原密码是否保留在源历史记录中变得无关紧要-因此,可能不值得修复。
如果要重写历史记录,可以使用多种工具,具体取决于受影响的历史记录数。所有这些的详细过程已在这里进行了无数次讨论,但总而言之:
如果这只是一个或两个引用的最新提交,则可以使用git commit --amend
如果它是简单的线性提交历史记录(可能不是很长的历史记录),则可以进行交互式的重新编制以编辑引入敏感数据的提交
对于历史记录不太复杂的更复杂情况,可以将git filter-branch
与树过滤器或索引过滤器一起使用
您可以使用一些专用工具,例如BFG Repo清洁器。
答案 1 :(得分:2)
尽管GitLab不如GitHub公开,但有关数据的一般规则适用于此:如果您向不可信的人提供了敏感/机密数据,则您的机密已经存在,您应该停止使用它。
这意味着关键问题不是(或至少现在不是)“我如何说服GitLab忘记我的秘密”,而是“我完全,完全信任GitLab服务器以及拥有此功能的其他所有人吗?一直都可以访问这些服务器?”如果答案为“否”,则无论如何都必须停止使用此机密。
也就是说,这是有关 Git本身存储数据的规则。假设您的GitLab服务器仅使用 Git(并且没有在其上构建其他一些东西,它们可能会增加访问数据的更多方式,从而为敏感/机密数据提供了更多方式)泄漏),您要做的就是说服GitLab服务器执行与您自己的Git中相同的操作。
Git的基础存储模型是存储库是Git称为对象的集合。每个对象都有唯一的哈希ID,并且是以下四种类型之一: blob , tree , commit 和带注释的标签。 blob 大致是文件数据。如果敏感/机密数据位于文件内部,则它们实际上位于blob对象中。一棵树配对—好于 pair ,但是现在让我们使用它 1 -每个文件的名称带有其Blob哈希ID,因此,如果文件的 name 是敏感/机密数据,则您的机密实际上位于树对象中。 commit 对象包含您的姓名,电子邮件地址,时间戳,日志消息以及某些先前的或 parent 提交的哈希ID,以及包含构成提交的快照 的文件的树。一个带有注释的标签对象与一个提交具有几乎相同的功能,只是它通常具有一个提交的哈希ID,而不是一个树对象。通常在这里存储一个PGP签名,将某些特定提交标记为“有福”,例如,称为2.3.4版或任何其他版本。
假设您的机密信息存在于一个名称本身不是机密信息的特定文件中,那么此时的目标是使Git停止使用保存该特定文件数据的Blob。为此,必须使对象本身成为未引用,然后使用git gc
使Git物理删除未引用的对象。在这一点上,通常对 reachability 稍作讨论是有用的,但我将其外包给Think Like (a) Git。让我们在这里说,通常,在您意外提交了一些秘密文件之后,Git查找 commit 对象的方式就是使用分支名称:
... <-F <-G <-H <--master
名称 master
包含提交H
的哈希ID 。提交H
包含其父提交{提交G
的哈希ID,因此Git要找到提交G
,首先要读取名称master
(它会产生哈希ID) H
),然后从数据库中读取提交对象(产生一个 tree 对象和一个 parent 提交哈希G
,以及日志消息以及您的姓名和电子邮件地址等),将G
的哈希值除掉以外的所有值,然后从数据库中读取实际的提交对象G
。如果您已要求Git从提交G
的 中获取某个特定文件(或更确切地说,该文件的内容),则它会使用G
的树来查找哈希ID包含该文件的Blob,然后从数据库中获取Blob对象,现在Git具有了内容。
因此,假设您的机密数据位于附加到提交H
的树上的blob中,而这些相同的数据不在 any 其他文件中,因此没有树连接到任何 other 提交都将具有该Blob的哈希ID。然后,要使H
本身未被引用,只需使名称master
指向G
而不是H
:
git checkout master
git reset --hard HEAD~1
现在您拥有:
...--E--F--G <-- master
\
H [abandoned]
但是,尽管H
没有持有其哈希ID的显而易见的名称,但我们尚未完成:git gc
不会-至少还没有 < / em>-删除H
,这就是事情开始变得复杂的地方。
如果H
中有有价值的文件,我们可以使用H
将git commit --amend
推到一边,以提交其父为I
的新提交G
而不是H
,并让master
指向I
:
... edit files, git add, git commit --amend ...
给予:
H [abandoned]
/
...--E--F--G--I <-- master
1 从技术上讲,每个树条目都有:
mode
,是文本字符串,例如100755
或100644
。如果条目用于子树,则字符串为40000
。(模式和名称以空格分隔,名称以ASCII NUL终止,而哈希ID以20个二进制字节编码。当Git切换到SHA-256时,这将不得不更改。我认为新格式尚未确定,但可以简单地说,例如使用0n
模式,其中n
是版本号,因为该模式为八进制由于前导零被抑制,因此现有的树将不会以01
作为模式;或者,它可能是NUL字节,后跟版本号,因为这也是当前无效的树条目。)在目录中,该树仅列出子树,对于常规文件,有两个值加一个哈希。对于符号链接,哈希ID仍然是斑点的ID,但是斑点的 content 是符号链接的 target ;对于子模块的gitlinks,哈希ID是 commit Git应该在子模块中git checkout
的哈希ID。
Git确实为您记住H
的部分,甚至在您git reset
离开之后,Git也称为 reflogs 。刷新日志会记住参考的上一个值。也就是说,在我们master
之前,分支名称H
可能指向git reset
。然后,在我们使用G
或I
放弃提交git reset --hard
之后,它立即指向git commit --amend
或H
。但是它用于指向H
,因此H
的哈希ID在名称master
的引用日志中。
@{1}
或@{yesterday}
语法是告诉Git查找这些引用日志值的方式。编写master@{1}
会告诉您的Git:在我的master
reflog中查找,并为我获取master
的前一值。该条目存在的事实会使您的Git保留提交H
,这将使您的Git保留包含机密信息的Blob。
实际上,至少有两个引用日志包含提交H
的哈希ID:一个用于master
,在master@{1}
中,另一个用于{{ 1}}本身。因此,如果您要说服Git真正放弃HEAD
的提交,并因此而放弃H
的树,并因此而放弃H
的树唯一的blob,则必须使这些reflog条目消失了。
通常,它们通常会在30天后自行消失。发生这种情况是因为每个reflog条目也都带有时间戳,并且H
将基于此时间戳(相对于计算机的当前时间)过期(并删除)旧的reflog条目。 master git reflog expire
命令为您运行git gc
,并将其设置为默认情况下30天之内无法到达的提交 2 过期。 (可实现的提交默认情况下为90天。)因此,在您自己的Git上,您需要运行:
git reflog expire
告诉您的Git:查找所有git reflog expire --expire-unreachable=now --all
之类的无法到达的提交,并立即终止其reflog条目。
2 从技术上讲,它是从参考的当前值不可访问的。也就是说,Git不会在这里测试全局可访问性,而是做一个更简单的测试:此reflog入口是否指向一个提交,该提交是引用本身现在指向的提交的祖先? / em>
即使H
和分支名称中的reflog条目都过期之后,您仍会发现自己的HEAD
不会立即丢弃blob对象。原因是所有 Git对象都有一个宽限期,在此期间git gc
不会修剪它们。默认的宽限期是14天。这使 all Git命令有一段时间可以创建对象而不必担心,只要它们通过链接所有这些对象完成在14天之内完成所有工作即可。到提交或标记对象或其他任何对象中,并使用适当的引用名称(例如分支或标记名称)记录该对象的哈希ID。
要使您误用git gc
提交的Blob消失,那么,您不仅需要终止无法访问的reflog条目,而且还告诉Git修剪对象,即使它们为零天数:
H
此修剪步骤是git prune --expire=now
实际删除对象的一部分,因此通过运行git gc
,您无需运行git prune
。 ({git gc
还会使reflog过期,依此类推,但是要协调所有操作以确保Git具有这些宽限期。由于我们绕过了所有宽限期,所以我们也绕过了git gc
。)< / p>
执行此操作时,请确保没有其他Git命令正在运行,因为它们可能正在创建对象,它们希望在完成工作时可以保留14天。
如果您的秘密存储在Git所谓的 loose 对象中,则上述步骤就足够了:该对象将完全消失,并且:
git gc
将不再找到该对象。该Git存储库中的任何地方都不再可用。
但并非所有对象都是松散的。最终,为了节省空间,Git将这些松散的对象打包到打包文件。打包文件中存储的对象将与同一打包文件中的其他对象压缩。 3 在这种情况下,如果您的机密数据已打包,则可以从打包文件中检索它们。
这通常不会很快发生,因此在包文件中很少出现刚刚提交的秘密。但是,如果已经发生了,清理它的唯一方法就是让Git 重新打包所有现有的打包文件。也就是说,您将让Git将包分解成其组成的松散对象,然后扔掉不需要的对象,然后构建一个新的(通常是单个)包文件-或至少使用具有这种效果的过程。用于重建软件包的Git命令为git rev-parse <hash-ID>
,它具有很多选项。由于时间有限,我在这里不再赘述。
3 在精简包中,对象可能会针对存储库中其他在包文件中不是 而是精简包的对象进行压缩仅用于获取和推送操作,然后通过添加缺少的碱基来“整理”它们。
要处理所有这些问题,您需要能够登录到GitLab服务器,因为这些维护Git命令(以及BFG,请参见下文)均不能通过获取或推送调用。特别是,尽管您可以使用客户端上的git repack
来使服务器上的名称git push -f
不再指向提交master
,但是您不能调用H
来进行松散物体消失。
如果以及当您执行登录服务器时,可以检查是否在此处为您的存储库启用了reflog。如果不是,则无需执行任何更新。您还可以通过查看git prune
目录来查看对象是否松动或打包。如果您的Blob哈希ID为.git/objects
,它将存储在名为0123456789...
的文件中。取消引用并修剪后,该文件将消失,您将完成。
使用the BFG repo cleaner可以避免很多麻烦。 BFG无论如何都不尊重任何宽限期,因为它有不同的目的。这也可以解决任何打包文件问题。与其他方法一样,此方法必须在服务器上运行,并且具有自己的怪癖(请参阅链接的问题和解答)。