关注了就能看到更多这么棒的文章哦~
By John Coggeshall
June 19, 2020
原文来自:https://lwn.net/Articles/823352/
主译:DeepL
多年来,Git源码管理系统一直在努力把它的Secure Hash Algorithm 1(SHA-1)算法替换成为更安全的SHA-256算法。最近,随着贡献者们实现了新的Git协议功能,项目向这一目标又迈进了一步。
从根本上说,Git 仓库是建立在哈希hash值上的——目前使用的是 SHA-1 算法。下面将简单介绍一下哈希值为什么对 Git 很重要。读者可能也会对我们之前的报道感兴趣,https://lwn.net/Articles/811068/ 这里有详细的介绍。
SHA-1哈希值是用来唯一地代表一个对象(比如一个源文件)内容的字符串,两个文件不应该产生相同的字符串。在Git中,每个对象的内容都有一个对应的哈希值。这些对象的具体目录结构存储在一个叫做tree object的对象中。这个tree object是一个有组织的哈希层次结构,每一个哈希值都指向版本库中某个对象的特定版本。如前所述,tree object本身在存储到版本库中时也是会有哈希值。当提交到版本库时,会逐步发生下面这些事件:
文件被赋予新的哈希值(如果它们被改变了)。
创建一个tree object,然后对其进行哈希处理,包含当前状态下所有文件的哈希值。
创建一个commit object并进行哈希处理,引用tree object的哈希值。
简而言之,Git里处处都在使用SHA-1哈希值,通过有效地创建一系列代表该仓库的对象的哈希值,随着时间推移而变更,类似于区块链技术,这样确保git仓库内容的完整性。
SHA-1此类散列算法的问题是,如果它产生的哈希值会有可能发生碰撞,它的使用价值就会被削弱。这里,碰撞指的是两批不同的数据产生了相同的哈希值。如果攻击者能够替换某一个对象的内容,使其仍然产生相同的哈希值,那么信任哈希值来唯一定义一个Git对象内容的这个前提就不再有效了。更糟糕的是,如果有人找到一种方法来智能地产生这些碰撞,比如说注入恶意代码,那么将会完全摧毁安全性,因为链中的文件可能被替换而不被人察觉。既然SHA-1实际上已经被人攻破了(https://www.computerworld.com/article/3173616/the-sha1-hash-function-is-now-completely-unsafe.html ),那么必须要脱离SHA-1了。随着最近的开发进程,距离替代SHA-1又近了一步。
主要推动从SHA-1到SHA-256的演进的是brian m. carlson,他多年来一直致力于实现这个升级。这并不是一件容易的事,最初的 Git 实现代码里面将 SHA-1 硬编码为它唯一能支持的算法,无数的Git仓库需要从 SHA-1 过渡到 SHA-256。此外,在这个过渡过程中,Git需要在单个仓库的范围内保持两种哈希算法之间互相通用,因为用户可能还在使用旧的Git客户端。
围绕这个过渡过程的问题很复杂。不同版本的Git客户端和服务器可能支持SHA-256,也可能不支持。而在未来一段时间内,所有的仓库都需要能够在两种算法下工作。这意味着Git需要以两种不同的方式来跟踪对象,并且无论Hash算法是什么,都能无缝地正确工作。例如,用户在引用提交时,哈希值经常被缩写为412e40d041而不是完整的412e40d041e861506bb3ac11a3a91e3这样,所以尽管SHA-256和SHA-1的哈希值长度不同,Git工具也无法完全凭借这一点来判断出是SHA-1的还是SHA-256的。
在最新一轮的补丁中,卡尔森提出了对通信协议逻辑的修改,从而方便处理这一过渡阶段的问题。这个补丁并不是最初过渡计划的一部分,但正如carlson所指出的那样,它成为了继续走下去的必要条件。
原本的计划是我们不会升级协议。在未来的某个时间点之前,我们会一直使用SHA-1来实现协议中的所有功能。然而,这样做需要大量的额外工作(可能还要加入几百个尚未写好的补丁),而且如果没法实现fetch和push功能的话,测试套件完全不可能被接受。因此,我决定实现一个object-format extension,这是最好的方法了。
此补丁集增强了Git客户端使用的pack protocol,从而可以区分所使用的Hash算法。这是通过新的 object-format
功能实现的。在协议文档的补丁中,carlson 将这个功能描述为 Git 用来告知它支持某种hash算法的方式:
该功能以哈希算法为参数,表明服务器可以支持此哈希算法[......]当客户端提供这个信息时,这表明它打算使用此类型的哈希算法进行通信。
如果客户端支持使用SHA-256的哈希算法,那么对协议进行这个改动之后就可以直接指定了。如果没有指定参数,Git将假设哈希值是以SHA-1的方式呈现的。
这为最常用的 Git 协议(git://)提供了一个清晰的发展方向。然而,它并没有解决那些其他一些使用方法下的问题,比如通过HTTP(http:// )进行通信,因为这种方法不提供capability信息。为了解决这些场景下的问题,代码实现里面试图通过观察哈希长度来猜测正在使用的哈希算法类型。Carlson指出,这种方法是可行的,但如果在未来的某个时刻,SHA-256被其他的算法所取代,同时也会产生256位的输出,这可能就是一个问题了。不过对此,Carlson表示,他相信未来可能取代SHA-256的哈希算法一定会比256位长:
另外两种情况是dump HTTP协议和bundles,这两种协议都没有object-format extension(因为它们没有提供任何capabilty信息),因此只能以其哈希长度来区分。如果将来我们需要使用另一种256位的算法的话,我们会碰到问题,但我打算即兴而为,希望我们将来会转向更长的算法来保证我们在量子计算时代的安全性。
Carlson承认,对于项目转向SHA-256所面临的技术挑战来说,他的解决方案并不理想。例如,在clone一个Git存储库时,父存储库所使用的哈希算法预先无法知道。Carlson的代码里面通过两个步骤来解决这个问题:
clone操作是必定有点棘手的,因为我们要先初始化一个Git仓库 然后再获取对端的refs,这时我们要了解远程侧支持的哈希算法是什么。我们通过再次调用更新哈希算法和仓库版本的函数来解决这个问题,一旦我们知道自己使用的是哪个Hash版本,就会重写这些数据。这是我能解决这个问题的最稳健的方法,虽然实现上有点难看。
随着这个milestone的达成,基于SHA-256的Git仓库的全面推进就指日可待了。这将是Git发展过程中的一个重要里程碑,也可以说是为Git的未来打下了坚实的基础。事实上,carlson列出了他计划中后续最后一部分patch可能包括的内容:
未来会有一些patch,其中包括最终一组test fixes(28个patch),再加上最后用来打开SHA-256的6个patch。
文章到了结尾了,这里需要注意的是,这次过渡如此艰难的原因之一是因为最初的Git实现时并没有考虑到今后会需要更新Hash算法。在SHA-256实现上投入的大部分工作都是在为这个最初的设计缺陷而进行弥补。随着这些改动基本完成,它不仅提供了一个替代SHA-1的方案,而且使Git从根本上可以不在意在使用哪种hash算法。这应该会让 Git 在未来需要用更强的算法来取代 SHA-256 时更加容易移植。
全文完
LWN文章遵循CC BY-SA 4.0许可协议。
欢迎分享、转载及基于现有协议再创作~
长按下面二维码关注,关注LWN深度文章以及开源社区的各种新近言论~