更新SHA-256的Git协议

2020-06-20 10:04:55

LWN订户已向您提供以下仅限订阅的内容。数以千计的用户依赖LWN获取来自Linux和自由软件社区的最好消息。如果您喜欢这篇文章,请考虑接受右边的试用报价。感谢您访问LWN.net!

免费试用LWN 1个月:无需付款或信用卡。现在激活您的试用订阅,看看为什么成千上万的读者订阅LWN.net。

多年来,Git源代码管理系统一直倾向于放弃安全散列算法1(SHA-1),转而采用更安全的SHA-256算法。最近,随着贡献者实现新的Git协议功能以实现过渡,该项目朝着这一目标又迈进了一步。

从根本上说,Git存储库是基于哈希值构建的--目前使用的是SHA-1算法。下面是散列值对Git重要性的简单解释;读者可能还会对我们以前的详细报道感兴趣。

SHA-1散列值是唯一表示对象(例如源文件)内容的字符串,任何两个文件都不应生成相同的字符串。在Git中,每个对象都有其内容的散列值表示。这些对象的特定目录结构存储在称为树对象的对象中。此树对象是有组织的散列层次结构,每个散列指向存储库中特定对象的特定版本。如前所述,树对象本身在存储到存储库中时也是散列的。当提交到存储库时,发生的基本步骤包括:

创建一个树对象,然后对其进行散列,其中包含处于当前状态的所有文件的所有散列。

简而言之,Git随时随地使用SHA-1散列来确保储存库内容的完整性,方法是随着时间的推移有效地创建代表该储存库的对象的散列值的链,类似于区块链技术。

SHA-1或任何散列算法的问题在于,如果它产生的散列可能导致冲突,那么它的有用性就会降低。在这种情况下,冲突意味着产生相同散列值的两段数据。如果攻击者能够替换对象的内容,使其仍然生成相同的散列值,那么信任散列值来唯一定义Git对象内容的想法就会破灭。更糟糕的是,如果有人找到一种方法来智能地产生这些冲突,比如注入恶意代码,其安全影响将是毁灭性的,因为它将允许链中的文件在不被注意的情况下被替换。由于SHA-1的实际妥协已经发生,因此远离SHA-1是很重要的。与最近的事态发展相比,这一过渡又近了一步。

从SHA-1到SHA-256的转变背后的主要力量是贡献者布莱恩·M·卡尔森(Brian M.Carlson),他多年来一直在为实现这一转变而努力。这并不是一项容易的任务;最初的Git实现将SHA-1硬编码为唯一受支持的算法,并且需要将无数的存储库从SHA-1转换到SHA-256。此外,在此过渡期间,Git需要在单个存储库的上下文中维护两个散列算法之间的互操作性,因为用户可能仍在使用较旧的Git客户端。

围绕这一过渡的问题是复杂的。不同版本的Git客户端和服务器可能支持SHA-256,也可能不支持SHA-256,在未来一段时间内,所有存储库都需要能够在这两种算法下工作。这意味着Git将需要以两种不同的方式跟踪对象,并且无论采用何种散列算法,都可以无缝地正确工作。例如,在引用提交时,用户通常将散列值缩写为412e40d041,而不是412e40d041e861506bb3ac11a3a91e3,因此,即使SHA-256和SHA-1散列值的长度不同,也只是略微有帮助。

在最新一轮补丁中,卡尔森建议修改处理过渡的通信协议逻辑。这个补丁听起来不像是最初过渡计划的一部分,但正如卡尔森指出的那样,它变成了前进的必要之处:

最初的计划是,在将来某个时候之前,我们不会升级协议,所有协议功能都将使用SHA-1。然而,这样做需要大量的额外工作(可能还需要包含数百个尚未编写的补丁),而且如果没有获取和推送存储库的方法,测试套件甚至不可能接近通过。因此,我决定实现对象格式扩展是最好的前进方式。

补丁程序集增强了Git客户端使用的包协议,包括跟踪散列算法。这是通过新的对象格式功能实现的。在协议文档的补丁中,Carlson将对象格式功能描述为Git指示支持特定散列算法的一种方式:

此功能以散列算法为参数,表示服务器支持给定的散列算法[.]。当由客户端提供时,这表示它打算使用给定的散列算法进行通信。

如果客户端支持使用SHA-256的哈希,则对协议的此更改可以直接指定。通过省略该功能,Git将假设提供的哈希值为SHA-1。

这为最常用的Git协议(git://)提供了一条清晰的前进路径。但是,它没有解决不太理想的方法,例如通过HTTP(http://),)进行通信,因为该方法不提供功能。为了解决这些情况,实现尝试通过查看散列长度来猜测正在使用的散列算法的类型。卡尔森指出,这是可行的,但如果在未来的某个时候,SHA-256被一种也能产生256位输出的不同算法所取代,可能会出现问题。然而,对于这一点,卡尔森表示,他相信任何有朝一日可能取代SHA-256的哈希算法都会比256位长:

另外两种情况是哑巴HTTP协议和捆绑包,它们都没有对象格式扩展(因为它们不提供任何功能),因此仅通过它们的散列长度来区分。如果将来我们需要使用另一种256位算法,我们将会遇到问题,但我计划不假思索,希望我们将来会转向更长的算法,以覆盖我们自己,以应对后量子安全。

卡尔森承认,他对迁移到SHA-256的项目所面临的技术挑战的解决方案并不理想。例如,在克隆存储库时,父存储库使用的散列算法事先是未知的。卡尔森的研究通过两个步骤绕过了这个问题:

克隆支持肯定有点棘手,因为我们要初始化一个存储库,然后获取引用,在这一点上,我们了解远程端支持的散列算法。我们通过第二次调用更新散列算法和存储库版本的代码来解决此问题,以便在知道正在使用的版本后重写该数据。这是我能处理这个问题的最有力的方法,但它仍然有点难看。

随着这一里程碑的到来,SHA-256存储库的全面工作实现即将结束。这将是Git发展过程中的一个重要里程碑,可以说它为未来奠定了坚实的基础。事实上,卡尔森列出了他预计最后的补丁可能包括的内容:

未来的其他系列包括最后一个测试修复系列(28个补丁程序),以及该系列中启用SHA-256支持的六个最终补丁程序。

最后,值得注意的是,这种过渡如此困难的原因之一是,最初的Git实现并不是为换出散列算法而设计的。投入到SHA-256实现中的大部分工作已经弥补了这个最初的设计缺陷。随着这些更改几乎完成,它不仅提供了SHA-1的替代方案,而且使Git从根本上与所使用的散列算法无关。这应该会使Git在未来出现需要用更强大的东西取代SHA-256时更具适应性。

你喜欢这篇文章吗?请接受我们的试用订阅优惠,以便能够看到更多类似的内容并参与讨论。

可以将$5$放在散列的开头,其数字与/etc/dow使用的数字相同!因此,6美元将用于sha512,以此类推。SHA1的挑战将保持不变,但未来将是容易的。

$5$的问题在于它会被外壳解释并损坏。命令带有散列值是很常见的,例如,GIT RESET HASH_VALUE,但有标准前缀是合理的。我曾提议将散列值在第一个字符中循环16个字符,这样0就变成了g,1变成了h,依此类推。然后,您可以从第一个字符确定使用哪种编码。您可以通过额外的旋转或编码更多字符来进一步扩展。

";485865fd0而不是412e40d041e861506bb3ac11a3a91e3";但485865fd0不是412e40d041e861506bb3ac11a3a91e3的前缀;如果使用前缀,该示例会更清楚。

>;但485865fd0不是412e40d041e861506bb3ac11a3a91e3的前缀;如果使用前缀,该示例会更清楚。谢谢-不知道是怎么回事。更新了文章,以便在示例中使用正确的速记值。