删除提交的 git 交互式变基是否会真正消除 API 密钥/秘密/密码的暴露?
Will a git interactive rebase that deletes a commit truly remove exposure of API keys / secrets / passwords?
重要的是不要在代码库中存储密码和秘密。
有时我们在开发应用程序时会硬编码 API 密码。我们删除它,通常是通过将它变成我们用 export
(Unix) 设置的环境变量。显然,更好的做法是从一开始就使用环境变量。
但是如果我们不那么小心并且我们提交了暴露密码的更改,会发生什么。
第一步是快速删除它们并提交并推送该更改。
好的
但是...
密码仍在 git 历史记录中,因此任何有权访问 git 存储库的人都可以获得密码。不好。
但是...
然后我们进行 git 交互式变基并 删除 (不是挤压)有问题的提交 = 历史记录中添加了密码的提交。
这会解决问题并确保密码不再可用 以任何方式 在 git 中吗?
当我拉出这个提交时,这将如何影响代码。如果除了带有密码的行之外还有其他代码,我大概需要重做那些会丢失的更改。如果提交是很久以前的,我可以想象如果任何提交也更改了同一行,就会出现问题。希望不会。
有很多关于如何删除敏感提交的答案,例如Remove sensitive files and their commits from Git history。任何好的答案都会警告您,无论如何都可能为时已晚,这是事实。没有太多关于 何时以及为什么 为时已晚的详细信息,但答案非常简单: 使用 并不是很多。这个答案的其余部分是关于何时以及为什么为时已晚,以及为什么仅使用交互式 rebase 删除提交是不够的。
问题的核心是无法更改提交,并且 Git 被连接以添加 new 提交。删除旧的/死的提交(和其他死的 objects)作为副作用发生,你几乎没有控制权。当你几乎做任何事情时——不管是什么:git commit --amend
、git rebase -i
、git reset --hard
、none 这很重要——任何 现有的 提交保留在您的提交数据库中,未更改,不受干扰,并且仍然可以通过其哈希 ID 获得。尽管如此, 可以真正删除提交。很难以可控和正确的方式做到这一点。
表示和查找提交
每个提交——实际上,每个 object1 在 Git 的主数据库中——都通过其哈希 ID 访问。分支中 last 提交的哈希 ID 在第二个较小的数据库中找到。本质上,像 master
这样的分支名称表示:master
的提示提交是 a123456...
,它提供提交的哈希 ID object 这样你——或者 Git—— 就可以回到主数据库并说:找我 object a123456...
.
每个提交都可以列出一些先前或 parent 提交的哈希 ID。也就是说,在获得 object a123456...
之后,您可以在其中四处寻找 parent 哈希 ID。如果 a123456...
的(单个)parent 散列 ID 是 9876543...
,那么你回到主数据库并说: *Get me object 9876543...
and你有以前的承诺。这就是您和 Git 可以从分支的 end 开始并向后工作的方式,一次提交一个:
... <-grandparent <-parent <-last-commit <--branchname
如果我们使用单个大写字母代表散列 ID,只需 记住 箭头(从 child 到 parent)始终指向向后,当你有多个分支时,我们会得到更容易绘制的东西:
...--E--F--G <-- master
\
H <-- develop
但在所有情况下,每当您对 "change" 您的历史记录进行某些操作时——例如,如果我们决定提交 G
是错误的并且必须被替换——你实际上 改变任何东西。相反,Git 实际上只是将错误的提交移开:
G
/
...--E--F--I <-- master
\
H <-- develop
主object数据库不会立即清除,如果你有任何方法来记住哈希ID在提交 G
之后,您可以通过该哈希 ID 向 Git 请求 G
。 Git 将呈现给您,因为 它在数据库中!
无论您如何 "delete" 或 "change" 提交,同样的描述都是正确的:Git 最终只是复制每个 other 提交,这样 "deleted" 或 "changed" 提交(这里, G
将被删除)现在在不同的 branch-line:
...--o--F--G--H--J--... <-- branch
变为:
G--H--J--... [previous branch, now abandoned]
/
...--o--F--H'-J'-... <-- branch
其中 H'
是 H
的副本,适用于 F
而不是 G
之后,J'
是 [=35= 的副本]适配到H'
之后,以此类推。同样,G
并不是真的 消失了 ,它只是被推到一边,连同它的所有后代。它的所有后代都被 slightly-altered 个副本替换,具有新的、不同的哈希 ID。
1有四种类型的object。 Commit、tree 和 blob object 一起工作以在提交中存储文件,使用 注释标签 object 制作第四种类型。每个提交指的是一棵树;如果需要,该树会引用其他 sub-trees,并引用 blob 来保存与该提交一起使用的文件。
删除提交
那么,什么时候——如何以及为什么——提交最终会消失?答案是Git有一个维护命令,git gc
,其工作是遍历[=149=]everyobject的整个主数据库,同时也遍历另一个可以找到object的所有名称的数据库。如果没有没有可以找到commit G
的名称,经过上述操作后,git gc
将确定是这种情况,并且最终—将G
踢出主数据库,使用操作系统的正常删除功能删除文件。2
更正式地说,git gc
要从主数据库中删除 object,object 必须 无法访问 。有关可达性概念的完整讨论,请参阅 Think Like (a) Git。不幸的是,对于您的特定用例,我们可以用来访问提交的名称集包括任何 refl 中的任何提交g.
2通常这是一个不安全的删除,因此如果您控制了底层存储介质,您仍然可以获得数据以这种方式返回,但现在显然要困难得多。无论如何,现在没有人可以通过哈希 ID 向 Git 存储库请求提交 G
。但是,当心支持快照的文件系统:您可以回到以前的快照并恢复快照时的整个存储库!
查找提交第 2 部分:reflogs
每个分支名都有一个reflog,比如master
,加上HEAD
一个。 (可能还有其他 reflog,但这是这里的两个重要的。)在上面的示例中,commit G
不再可以从名称 master
访问,但仍然有两个 reflog 条目,master@{1}
和 HEAD@{1}
,这两个服务器都可以找到提交 G
。所以 git gc
不会删除提交 G——无论如何,现在还不会。
找到 G
的 reflog 条目最终将被删除 。特别是,git reflog expire
自动删除 sufficiently-old,因此 expired reflog 条目。您可以配置多长时间足够大,但默认为 30 天或 90 天,3,在本例中为 30 天。
this 的意思是,默认情况下,G
会一直存在,直到 git gc
使用 git reflog
删除 reflog 条目,一旦他们足够大——即从现在起至少 30 天。如果你想加快那部分的速度,你可以使用 git reflog
(见 the documentation)来更快地删除或使 G
的条目过期;或查看下面的克隆。
一旦 reflog 条目消失,G
确实(全局)无法访问,git gc
将删除它。你可以看出这是因为 git show <em>hash</em>
and git rev-parse <em>hash</em>
会告诉你他们不知道你在说什么哈希 ID。
还请记住,如果您的 Git 联系了另一个 Git,您的 Git 可能已经向其他 Git 提交了 G
。特别是,当您 运行 git push
时,您 Git 调用另一个 Git 并向它们提供提交。如果您已经 他们 提交 G
,您在自己的存储库中所做的任何事情都无法收回。如果您允许其他用户从您的存储库中 git fetch
,他们可能已经获取了 G
的副本,同样,您在自己的存储库中所做的任何事情都无法收回:您必须说服 他们 放弃提交。
Reflogs 不会被 git clone
复制,所以另一种无需等待即可摆脱 G
的方法是克隆您自己的存储库。 git clone
所做的是创建一个 new 存储库,然后从原始存储库中获取。提取获取的提交是那些可以从源存储库公开的名称访问的提交。因此,与其手动使一些 reflog 条目过期然后 运行ning git gc
,不如克隆自己的存储库。这里有一个缺点:您失去了所有 reflog 的安全网,并且您自己的分支名称成为新存储库的 origin/*
名称。4
3此处 30 天和 90 天之间的选择取决于引用本身指向的提交是否可以访问 reflog 中的值。在这种情况下,名称 master
指向提交 I
,例如,并且不可能从 I
返回到 G
,因此 [=48] 中的值=],指向 G
,无法从 master
上的值访问。这意味着到期时间是 gc.reflogExpireUnreachable
——默认为 30 天——而不是 gc.reflogExpire
,后者默认为 90 天。
请注意,我们再次通过有向图依赖于 可达性 的概念。这是理解Git.
的关键之一
4你可以使用 git clone --mirror
,但这会让你得到一个 bare 存储库,并且一个默认值不合适 fetch
设置。然后你可以修复这两个,但是如果你知道如何做所有这些,你可能想要使用 --mirror
以外的东西。
总结
如果:
- 您没有与任何人分享不需要的提交(没有提取或推送),和
- 您删除所有对提交的引用,或者等待 30 天,然后 运行
git gc
然后 提交将真正消失,没有通过 file-system 级别快照的任何形式的复活。您可以将哈希 ID 提供给 git show
或 git rev-parse
以验证它是否已消失。但是,如果提交可能已被复制到其他任何地方,您将无法再对其进行任何控制。
安全的默认设置是假设如果提交在任何时间段内对任何其他人可见,它已经被复制,并且其中的秘密是不再是秘密。
重要的是不要在代码库中存储密码和秘密。
有时我们在开发应用程序时会硬编码 API 密码。我们删除它,通常是通过将它变成我们用 export
(Unix) 设置的环境变量。显然,更好的做法是从一开始就使用环境变量。
但是如果我们不那么小心并且我们提交了暴露密码的更改,会发生什么。
第一步是快速删除它们并提交并推送该更改。
好的
但是...
密码仍在 git 历史记录中,因此任何有权访问 git 存储库的人都可以获得密码。不好。
但是...
然后我们进行 git 交互式变基并 删除 (不是挤压)有问题的提交 = 历史记录中添加了密码的提交。
这会解决问题并确保密码不再可用 以任何方式 在 git 中吗?
当我拉出这个提交时,这将如何影响代码。如果除了带有密码的行之外还有其他代码,我大概需要重做那些会丢失的更改。如果提交是很久以前的,我可以想象如果任何提交也更改了同一行,就会出现问题。希望不会。
有很多关于如何删除敏感提交的答案,例如Remove sensitive files and their commits from Git history。任何好的答案都会警告您,无论如何都可能为时已晚,这是事实。没有太多关于 何时以及为什么 为时已晚的详细信息,但答案非常简单: 使用 并不是很多。这个答案的其余部分是关于何时以及为什么为时已晚,以及为什么仅使用交互式 rebase 删除提交是不够的。
问题的核心是无法更改提交,并且 Git 被连接以添加 new 提交。删除旧的/死的提交(和其他死的 objects)作为副作用发生,你几乎没有控制权。当你几乎做任何事情时——不管是什么:git commit --amend
、git rebase -i
、git reset --hard
、none 这很重要——任何 现有的 提交保留在您的提交数据库中,未更改,不受干扰,并且仍然可以通过其哈希 ID 获得。尽管如此, 可以真正删除提交。很难以可控和正确的方式做到这一点。
表示和查找提交
每个提交——实际上,每个 object1 在 Git 的主数据库中——都通过其哈希 ID 访问。分支中 last 提交的哈希 ID 在第二个较小的数据库中找到。本质上,像 master
这样的分支名称表示:master
的提示提交是 a123456...
,它提供提交的哈希 ID object 这样你——或者 Git—— 就可以回到主数据库并说:找我 object a123456...
.
每个提交都可以列出一些先前或 parent 提交的哈希 ID。也就是说,在获得 object a123456...
之后,您可以在其中四处寻找 parent 哈希 ID。如果 a123456...
的(单个)parent 散列 ID 是 9876543...
,那么你回到主数据库并说: *Get me object 9876543...
and你有以前的承诺。这就是您和 Git 可以从分支的 end 开始并向后工作的方式,一次提交一个:
... <-grandparent <-parent <-last-commit <--branchname
如果我们使用单个大写字母代表散列 ID,只需 记住 箭头(从 child 到 parent)始终指向向后,当你有多个分支时,我们会得到更容易绘制的东西:
...--E--F--G <-- master
\
H <-- develop
但在所有情况下,每当您对 "change" 您的历史记录进行某些操作时——例如,如果我们决定提交 G
是错误的并且必须被替换——你实际上 改变任何东西。相反,Git 实际上只是将错误的提交移开:
G
/
...--E--F--I <-- master
\
H <-- develop
主object数据库不会立即清除,如果你有任何方法来记住哈希ID在提交 G
之后,您可以通过该哈希 ID 向 Git 请求 G
。 Git 将呈现给您,因为 它在数据库中!
无论您如何 "delete" 或 "change" 提交,同样的描述都是正确的:Git 最终只是复制每个 other 提交,这样 "deleted" 或 "changed" 提交(这里, G
将被删除)现在在不同的 branch-line:
...--o--F--G--H--J--... <-- branch
变为:
G--H--J--... [previous branch, now abandoned]
/
...--o--F--H'-J'-... <-- branch
其中 H'
是 H
的副本,适用于 F
而不是 G
之后,J'
是 [=35= 的副本]适配到H'
之后,以此类推。同样,G
并不是真的 消失了 ,它只是被推到一边,连同它的所有后代。它的所有后代都被 slightly-altered 个副本替换,具有新的、不同的哈希 ID。
1有四种类型的object。 Commit、tree 和 blob object 一起工作以在提交中存储文件,使用 注释标签 object 制作第四种类型。每个提交指的是一棵树;如果需要,该树会引用其他 sub-trees,并引用 blob 来保存与该提交一起使用的文件。
删除提交
那么,什么时候——如何以及为什么——提交最终会消失?答案是Git有一个维护命令,git gc
,其工作是遍历[=149=]everyobject的整个主数据库,同时也遍历另一个可以找到object的所有名称的数据库。如果没有没有可以找到commit G
的名称,经过上述操作后,git gc
将确定是这种情况,并且最终—将G
踢出主数据库,使用操作系统的正常删除功能删除文件。2
更正式地说,git gc
要从主数据库中删除 object,object 必须 无法访问 。有关可达性概念的完整讨论,请参阅 Think Like (a) Git。不幸的是,对于您的特定用例,我们可以用来访问提交的名称集包括任何 refl 中的任何提交g.
2通常这是一个不安全的删除,因此如果您控制了底层存储介质,您仍然可以获得数据以这种方式返回,但现在显然要困难得多。无论如何,现在没有人可以通过哈希 ID 向 Git 存储库请求提交 G
。但是,当心支持快照的文件系统:您可以回到以前的快照并恢复快照时的整个存储库!
查找提交第 2 部分:reflogs
每个分支名都有一个reflog,比如master
,加上HEAD
一个。 (可能还有其他 reflog,但这是这里的两个重要的。)在上面的示例中,commit G
不再可以从名称 master
访问,但仍然有两个 reflog 条目,master@{1}
和 HEAD@{1}
,这两个服务器都可以找到提交 G
。所以 git gc
不会删除提交 G——无论如何,现在还不会。
找到 G
的 reflog 条目最终将被删除 。特别是,git reflog expire
自动删除 sufficiently-old,因此 expired reflog 条目。您可以配置多长时间足够大,但默认为 30 天或 90 天,3,在本例中为 30 天。
this 的意思是,默认情况下,G
会一直存在,直到 git gc
使用 git reflog
删除 reflog 条目,一旦他们足够大——即从现在起至少 30 天。如果你想加快那部分的速度,你可以使用 git reflog
(见 the documentation)来更快地删除或使 G
的条目过期;或查看下面的克隆。
一旦 reflog 条目消失,G
确实(全局)无法访问,git gc
将删除它。你可以看出这是因为 git show <em>hash</em>
and git rev-parse <em>hash</em>
会告诉你他们不知道你在说什么哈希 ID。
还请记住,如果您的 Git 联系了另一个 Git,您的 Git 可能已经向其他 Git 提交了 G
。特别是,当您 运行 git push
时,您 Git 调用另一个 Git 并向它们提供提交。如果您已经 他们 提交 G
,您在自己的存储库中所做的任何事情都无法收回。如果您允许其他用户从您的存储库中 git fetch
,他们可能已经获取了 G
的副本,同样,您在自己的存储库中所做的任何事情都无法收回:您必须说服 他们 放弃提交。
Reflogs 不会被 git clone
复制,所以另一种无需等待即可摆脱 G
的方法是克隆您自己的存储库。 git clone
所做的是创建一个 new 存储库,然后从原始存储库中获取。提取获取的提交是那些可以从源存储库公开的名称访问的提交。因此,与其手动使一些 reflog 条目过期然后 运行ning git gc
,不如克隆自己的存储库。这里有一个缺点:您失去了所有 reflog 的安全网,并且您自己的分支名称成为新存储库的 origin/*
名称。4
3此处 30 天和 90 天之间的选择取决于引用本身指向的提交是否可以访问 reflog 中的值。在这种情况下,名称 master
指向提交 I
,例如,并且不可能从 I
返回到 G
,因此 [=48] 中的值=],指向 G
,无法从 master
上的值访问。这意味着到期时间是 gc.reflogExpireUnreachable
——默认为 30 天——而不是 gc.reflogExpire
,后者默认为 90 天。
请注意,我们再次通过有向图依赖于 可达性 的概念。这是理解Git.
的关键之一4你可以使用 git clone --mirror
,但这会让你得到一个 bare 存储库,并且一个默认值不合适 fetch
设置。然后你可以修复这两个,但是如果你知道如何做所有这些,你可能想要使用 --mirror
以外的东西。
总结
如果:
- 您没有与任何人分享不需要的提交(没有提取或推送),和
- 您删除所有对提交的引用,或者等待 30 天,然后 运行
git gc
然后 提交将真正消失,没有通过 file-system 级别快照的任何形式的复活。您可以将哈希 ID 提供给 git show
或 git rev-parse
以验证它是否已消失。但是,如果提交可能已被复制到其他任何地方,您将无法再对其进行任何控制。
安全的默认设置是假设如果提交在任何时间段内对任何其他人可见,它已经被复制,并且其中的秘密是不再是秘密。