删除提交的 git 交互式变基是否能真正消除 API 密钥/秘密/密码的暴露?

Mic*_*ant 3 git passwords secret-key

重要的是不要将密码和秘密存储在代码存储库中。

有时,我们在开发应用程序时会硬编码 API 密码。我们通常通过将其转换为我们设置的环境变量export(Unix)来删除它。显然,更好的做法是从一开始就使用环境变量。

但是,如果我们不那么小心并且提交了导致密码暴露的更改,会发生什么情况。
第一步是快速删除它们并提交并推动更改。
好的

但...

密码仍然在 git 历史记录中,因此任何有权访问 git 存储库的人都可以获得密码。不好。

但...

然后,我们执行 git 交互式变基并删除(而不是压缩)有问题的提交 = 在历史记录中添加了密码的提交。

这会解决问题并确保密码在 git 中不再以任何方式可用吗?

当我拉出此提交时,这将如何影响代码。如果除了带有密码的行之外还有其他代码,我可能需要重做那些会丢失的更改。如果提交是很多年前的事情,那么如果此后的任何提交也更改了同一行,我可以想象会出现问题。但愿不会。

tor*_*rek 7

关于如何删除敏感提交有很多答案,例如,从 Git 历史记录中删除敏感文件及其提交。任何好的答案都会警告您,无论如何可能已经太晚了,这是事实。没有太多人详细讨论何时以及为何为时已晚,但答案非常简单:这并不是没有多大用处。这个答案的其余部分是关于何时以及为什么为时已晚,以及为什么仅使用交互式变基删除提交是不够的。

\n\n

问题的核心是提交无法更改,而 Git 会添加新的提交。删除旧的/死的提交(以及其他死的对象)会产生副作用,您几乎无法控制。当您执行几乎任何操作时\xe2\x80\x94,无论什么:git commit --amendgit rebase -igit reset --hard,这些都不重要\xe2\x80\x94任何现有提交都会保留在您的提交数据库中,未更改、不受干扰,并且仍然可以通过其哈希 ID 获取。尽管如此,确实可以删除提交。只是很难以受控且正确的方式做到这一点。

\n\n

代表和查找提交

\n\n

每个提交\xe2\x80\x94实际上Git主数据库\xe2\x80\x94中的每个对象1都是通过其哈希ID来访问的。分支中最后一次提交的哈希 ID可在第二个较小的数据库中找到。本质上,分支名称如下master所示:的tip commit masterisa123456...,它提供提交对象的哈希ID,以便您\xe2\x80\x94或Git\xe2\x80\x94可以返回到主数据库并说:Get me目的a123456...

\n\n

每个提交都可以列出一些先前提交或父提交的哈希 ID 。也就是说,获得 object 后a123456...,您可以在其中查找父哈希 ID。如果 的(单个)父哈希 ID 是a123456...9876543...那么您将返回到主数据库并说:*获取我对象9876543...并且您拥有之前的提交。这就是你\xe2\x80\x94和Git\xe2\x80\x94如何从分支末尾开始向后工作,一次提交一个:

\n\n
... <-grandparent <-parent <-last-commit   <--branchname\n
Run Code Online (Sandbox Code Playgroud)\n\n

如果我们使用单个大写字母来代表哈希 ID,并且记住箭头(从子级到父级)始终指向向后,那么当您有多个分支时,我们会得到更容易绘制的东西:

\n\n
...--E--F--G   <-- master\n         \\\n          H  <-- develop\n
Run Code Online (Sandbox Code Playgroud)\n\n

但在所有情况下,每当您执行某些操作来“更改”您的历史记录\xe2\x80\x94时,例如,如果我们认为提交G是错误的并且必须替换\xe2\x80\x94,那么您实际上并没有更改任何内容。相反,Git 实际上只是将错误的提交移开:

\n\n
          G\n         /\n...--E--F--I   <-- master\n         \\\n          H  <-- develop\n
Run Code Online (Sandbox Code Playgroud)\n\n

主对象数据库不会立即清除,如果你有办法记住 commit 的哈希 ID ,你可以通过该哈希 IDG向 Git 询问。GGit 会将其呈现给您,因为它在数据库中!

\n\n

无论您如何“删除”或“更改”提交,同样的描述都是正确的:Git 最终会复制所有其他提交,以便“删除”或“更改”提交(此处G是要删除的)现在在不同的支线上:

\n\n
...--o--F--G--H--J--...   <-- branch\n
Run Code Online (Sandbox Code Playgroud)\n\n

变成:

\n\n
          G--H--J--...   [previous branch, now abandoned]\n         /\n...--o--F--H\'-J\'-...   <-- branch\n
Run Code Online (Sandbox Code Playgroud)\n\n

其中是适应之后的H\'副本而不是,是适应之后的副本,依此类推。再说一次,它并没有真正消失,它只是和它的所有后代一起被推到一边。它的所有后代都被稍微改变的副本所取代,并具有新的、不同的哈希 ID。HFGJ\'JH\'G

\n\n
\n\n

1对象有四种类型。 Committreeblob对象一起工作以将文件存储在提交中,带注释的标记对象构成第四种类型。每个提交引用一棵树;如果需要,该树引用其他子树,并引用 blob 来保存与该提交相关的文件。

\n\n
\n\n

删除提交

\n\n

那么,何时\xe2\x80\x94 以及如何以及为何\xe2\x80\x94 提交最终会消失?答案是,Git 有一个维护命令,git gc它的工作是遍历每个对象的整个主数据库,同时还遍历可以查找对象的所有名称的另一个数据库。如果没有名称可以找到 commit ,那么G在进行上述操作后,git gc将确定是这种情况,并且\xe2\x80\x94最终将\xe2\x80\x94G踢出主数据库,使用任何操作系统正常的删除功能是删除文件。2

\n\n

更正式地说,要从git gc主数据库中删除对象,该对象必须是不可访问的。有关可达性概念的详细讨论,请参阅Think Like (a) Git。不幸的是,对于您的特定用例,我们可以到达提交的名称集包括任何reflog中的任何提交。

\n\n
\n\n

2通常,这是一种不安全的删除,因此,如果您可以控制底层存储介质,您仍然可以通过这种方式取回数据,但现在显然要困难得多。无论如何,现在没有人可以直接要求 Git 存储库G通过哈希 ID 进行提交。不过,请注意支持快照的文件系统:您可以返回到以前的快照并将整个存储库恢复为快照时的状态!

\n\n
\n\n

查找提交第 2 部分:引用日志

\n\n

每个分支名称都有一个引用日志,例如master,再加上一个HEAD。(可能还有其他引用日志,但这是这里的两个重要的。)在上面的示例中,G无法再从 name 访问commit master,但仍然有两个引用日志条目master@{1}HEAD@{1},这两个条目都是要查找 commit 的服务器G。所以git gc无论如何,还不会删除提交 G\xe2\x80\x94。

\n\n

找到的引用日志条目最终G 被删除。特别是,git reflog expire自动删除足够旧且因此过期的引用日志条目。您可以配置多久才算足够老,但默认为 30 天或 90 天,即3 天,在本例中为 30 天。

\n\n

这意味着,默认情况下,一旦引用日志条目足够老\xe2\x80\x94,即从现在起至少 30 天,它将一直保留到G用户git gc删除引用日志条目为止。如果您想加快该部分的速度,git reflog可以使用git reflog(请参阅文档)更快地删除或使条目过期;G或参见下面的克隆。

\n\n

一旦引用日志条目消失,那么G实际上(全局)无法访问,git gc将删除它。你可以看出这已经发生了,因为和会告诉你他们不知道你在谈论什么哈希 ID。git show hashgit rev-parse hash

\n\n

还要记住,如果您的 Git 联系了另一个 Git,您的 Git 可能已经给出了其他 Git commit G。特别是,当您运行 时git push,您的 Git 会调用另一个 Git 并向它们提供提交。如果您已经给了他们commit G,那么您在自己的存储库中所做的任何操作都无法收回。如果您允许其他用户访问git fetch您的存储库,他们可能已经获取了 的副本G,并且您在自己的存储库中所做的任何操作都无法取回该副本:您必须说服他们放弃提交。

\n\n

引用日志不会被 复制git clone,因此无需等待即可摆脱 的另一种方法G是克隆您自己的存储库。所做git clone的就是创建一个新的存储库,然后从原始存储库中获取。提取获取的提交是可从源存储库公开的名称访问的提交。git gc因此,您可以克隆自己的存储库,而不是手动使某些引用日志条目过期然后运行。这里有一个缺点:您失去了所有引用日志的安全网,并且您自己的分支名称成为新存储库的origin/*名称。4

\n\n
\n\n

3此处 30 到 90 天之间的选择取决于引用日志中的值是否可以从引用本身指向的提交访问。例如,在这种情况下,名称master指向 commit I,并且不可能从 回溯IG,因此无法从 上的值访问指向 的master@{1}值。这意味着过期时间是\xe2\x80\x94(默认为 30 天)\xe2\x80\x94 而不是,默认为 90 天。Gmastergc.reflogExpireUnreachablegc.reflogExpire

\n\n

请注意,我们再次依赖于通过有向图可达性的概念。这是理解 Git 的关键之一。

\n\n

4您可以使用git clone --mirror,但这会给您带来一个裸露的存储库,并且其默认fetch设置不合适。然后您可以修复这两个问题,但如果您知道如何执行所有这些操作,您可能会想要使用其他方法--mirror

\n\n
\n\n

概括

\n\n

如果:

\n\n
    \n
  • 您尚未与任何人共享不需要的提交(没有提取或推送),并且
  • \n
  • 您删除对提交的所有引用,或等待 30 天,然后运行git gc
  • \n
\n\n

那么提交将真正消失,没有通过文件系统级快照进行任何形式的复活。您可以将哈希 ID 提供给git showgit rev-parse来验证它是否已消失。但是,如果提交可能已被复制到其他地方,那么您将不再对此有任何控制权。

\n\n

安全的默认设置是假设如果提交在任何时间段内对其他人可见,则它被复制,并且其中的秘密不再是秘密。

\n