这可能在现实世界中从未发生过,也可能从未发生过,但是让我们考虑一下:假设您有一个git存储库,提交并非常不走运:一个blob最终具有相同的SHA-1作为您存储库中已经存在的另一个。问题是,Git将如何处理?只是失败了?找到一种方法链接两个Blob,并根据上下文检查需要哪个Blob?
比实际的问题更有趣,但我发现这个问题很有趣。
这可能在现实世界中从未发生过,也可能从未发生过,但是让我们考虑一下:假设您有一个git存储库,提交并非常不走运:一个blob最终具有相同的SHA-1作为您存储库中已经存在的另一个。问题是,Git将如何处理?只是失败了?找到一种方法链接两个Blob,并根据上下文检查需要哪个Blob?
比实际的问题更有趣,但我发现这个问题很有趣。
Answers:
我做了一个实验,以找出Git在这种情况下的确切表现。版本2.7.9〜rc0 + next.20151210(Debian版本)。我基本上只是通过应用以下diff并重建git来将哈希大小从160位减小为4位:
--- git-2.7.0~rc0+next.20151210.orig/block-sha1/sha1.c
+++ git-2.7.0~rc0+next.20151210/block-sha1/sha1.c
@@ -246,6 +246,8 @@ void blk_SHA1_Final(unsigned char hashou
blk_SHA1_Update(ctx, padlen, 8);
/* Output hash */
- for (i = 0; i < 5; i++)
- put_be32(hashout + i * 4, ctx->H[i]);
+ for (i = 0; i < 1; i++)
+ put_be32(hashout + i * 4, (ctx->H[i] & 0xf000000));
+ for (i = 1; i < 5; i++)
+ put_be32(hashout + i * 4, 0);
}
然后我做了一些提交,并注意到了以下内容。
对于#2,当您运行“ git push”时,通常会收到如下错误:
error: object 0400000000000000000000000000000000000000 is a tree, not a blob
fatal: bad blob object
error: failed to push some refs to origin
要么:
error: unable to read sha1 file of file.txt (0400000000000000000000000000000000000000)
如果删除文件,然后运行“ git checkout file.txt”。
对于#4和#6,通常会出现如下错误:
error: Trying to write non-commit object
f000000000000000000000000000000000000000 to branch refs/heads/master
fatal: cannot update HEAD ref
运行“ git commit”时。在这种情况下,您通常可以再次键入“ git commit”,因为这将创建一个新的哈希(由于更改的时间戳)
对于#5和#9,通常会出现如下错误:
fatal: 1000000000000000000000000000000000000000 is not a valid 'tree' object
运行“ git commit”时
如果有人尝试克隆您损坏的存储库,则他们通常会看到类似以下内容的信息:
git clone (one repo with collided blob,
d000000000000000000000000000000000000000 is commit,
f000000000000000000000000000000000000000 is tree)
Cloning into 'clonedversion'...
done.
error: unable to read sha1 file of s (d000000000000000000000000000000000000000)
error: unable to read sha1 file of tullebukk
(f000000000000000000000000000000000000000)
fatal: unable to checkout working tree
warning: Clone succeeded, but checkout failed.
You can inspect what was checked out with 'git status'
and retry the checkout with 'git checkout -f HEAD'
让我“担心”的是,在两种情况下(2,3),该存储库在没有任何警告的情况下已损坏,而在3种情况下(1,7,8),一切似乎正常,但是该存储库的内容与您期望的不同成为。克隆或拉人的内容将与您拥有的内容不同。情况4,5,6和9可以,因为它会因错误而停止。我想如果它至少在所有情况下都因错误而失败,那就更好了。
原始答案(2012)(请参阅shattered.io
下面的2017 SHA1碰撞)
这从莱纳斯旧(2006年)的回答仍然可能是相关的:
不。如果它具有相同的SHA1,则意味着当我们从另一端收到对象时,我们不会覆盖已经拥有的对象。
因此,发生的事情是,如果我们遇到冲突,则任何特定存储库中的“较早”对象将始终被覆盖。但请注意,“较早版本”显然是每个存储库,因为git对象网络生成的DAG并非完全有序,因此在直接祖先的情况下,如果不同的存储库会就“较早版本”达成一致,如果一个对象是通过不同的分支而不是直接相关的分支而来的,两个不同的存储库显然可以按不同的顺序获得这两个对象。
但是,从安全的角度来看,“早期将被覆盖”是您想要的:记住git模型是您应该主要只信任自己的存储库。
因此,如果您执行“git pull
”,则从定义上讲,新传入的对象不如现有对象具有更高的信任度,因此,允许新对象替换旧对象是错误的。因此,您有两种情况发生碰撞:
在不经意的那种,你不知何故是非常非常不走运,而两个文件最终具有相同SHA1。
那时发生的事情是,当您提交该文件(或执行“git-update-index
”将其移入索引但尚未提交)时,将计算新内容的SHA1,但由于它与旧对象匹配,不会创建一个新对象,并且commit-or-index最终指向旧对象。
您不会立即注意到(因为索引将与旧对象SHA1相匹配,这意味着类似“git diff
”将使用检出的副本),但是如果您执行树级比较(或执行克隆)或拉出或强制执行结帐),您会突然发现该文件已更改为某种内容完全不同于您的预期。
因此,您通常会很快注意到这种碰撞。
在相关新闻中,问题是如何处理无意中的碰撞。
首先,让我提醒人们,无意中的碰撞真的非常不可能,所以我们很可能永远都不会看到它。宇宙
但是,如果发生这种情况,那还不是世界末日:您最有可能要做的就是更改发生轻微冲突的文件,然后使用更改后的内容强制执行新提交(添加注释,表示“/* This line added to avoid collision */
”),然后向git讲解已证明是危险的魔术SHA1。
因此,在几百万年的时间里,也许我们必须向git中添加一个或两个“中毒”的SHA1值。这不太可能是维护问题;)该攻击者那种碰撞,因为有人打破(或野蛮强制)SHA1。
这一个显然是一个很多比无意样的可能性较大,但顾名思义它总是一个“远程”库。如果攻击者可以访问本地存储库,那么他将有更简单的方法来欺骗您。
所以在这种情况下,碰撞是完全不成问题:你会得到一个“坏”的库是从什么攻击者预想的不同,但因为你从来没有真正用他的碰撞物体,这是字面上没有什么不同攻击者根本没有发现碰撞,但仅使用您已有的对象即可(即,它等同于生成相同SHA1的相同文件的“平凡”冲突)。
经常提到使用SHA-256的问题,但目前尚未采取行动(2012年)。
注意:从2018和Git 2.19开始,代码被重构为使用SHA-256。
注(幽默):你可以强制提交到一个特定的SHA1 前缀,项目gitbrute从布拉德·菲茨帕特里克(bradfitz
)。
gitbrute brute强制使用一对author + commititter时间戳,以使生成的git commit具有所需的前缀。
示例:https://github.com/bradfitz/deadbeef
丹尼尔Dinnyes指出,在评论中,以7.1的Git工具-修正选择,其中包括:
更有可能的是,您的编程团队的每个成员都将在同一晚的不相关事件中被狼袭击并杀死。
即使是最近(二月2017)shattered.io
证明伪造SHA1碰撞的可能性:
(见得多了,我单独的答案,包括Linus Torvalds的Google+信息)
有关更多信息,请参见Valerie Anita Aurora的 “ 加密哈希函数的生存期 ” 。
她在该页面中指出:
Google花了6500年的CPU时间和110年的GPU时间说服每个人,我们都需要停止对安全关键型应用程序使用SHA-1。
还因为它很酷
在下面的单独答案中查看更多内容。
/* This line added to avoid collision */
:D 仍然可以散列相同:D可以赢得两次彩票:P
/* This line added to avoid collision of the avoid collision line */
根据Pro Git:
如果您确实提交了一个哈希值与存储库中先前对象相同的SHA-1值的对象,则Git将在您的Git数据库中看到该先前对象,并假定它已被写入。如果您尝试在某个时刻再次检出该对象,则将始终获得第一个对象的数据。
因此它不会失败,但也不会保存新对象。
我不知道在命令行上会怎样,但这肯定会造成混乱。
再往下一点,该参考文献试图说明这种碰撞的可能性:
这是一个示例,可让您大致了解发生SHA-1冲突的情况。如果地球上所有的65亿人都在编程,并且每一秒钟产生的代码都相当于整个Linux内核历史记录(100万个Git对象)并将其推入一个巨大的Git存储库,则需要5年的时间该存储库包含足够多的对象,因此一次SHA-1对象冲突的可能性为50%。更有可能的是,您的编程团队的每个成员都将在同一晚的不相关事件中被狼袭击并杀死。
作为我2012年以前的回答,现在(五年后的2017年2月)是一个实际的SHA-1与shattered.io碰撞的示例,您可以在其中制作两个碰撞的PDF文件:即获得SHA-第一个PDF文件上的1个数字签名,也可以被滥用为第二个PDF文件上的有效签名。
另请参见“ 多年以来,广泛使用的SHA1功能已经失效 ”和此图。
2月26日更新:Linus 在Google+帖子中确认了以下几点:
(1)首先-天空没有落下。将加密哈希用于安全签名之类的代码与为内容可寻址系统(如git)生成“内容标识符”的代码之间存在很大的区别。
(2)其次,这种特殊的SHA1攻击的性质意味着它实际上很容易缓解,并且已经发布了两组缓解措施。
(3)最后,实际上是向其他哈希表的过渡相当简单,不会破坏世界-甚至是旧的git存储库。
关于该过渡,请参阅Q1 2018 Git 2.16添加了表示哈希算法的结构。该过渡的实施已经开始。
从Git 2.19(Q3 2018)开始,Git选择SHA-256作为NewHash,并且正在将其集成到代码中(这意味着SHA1仍然是默认值(Q2 2019,Git 2.21),但是SHA2将是继任者)
原始答案(2月25日)但是:
git-svn
,虽然。或者更确切地说是svn本身,如此处所示。git fsck
,正如今天的Linus Torvalds提到的那样。git fsck
将警告有关在提交消息后隐藏不透明数据的提交消息NUL
(尽管NUL
在欺诈性文件中并不总是存在)。transfer.fsck
,但是GitHub会打开:如果对象格式错误或链接断开,任何推送都会中止。尽管...有一个原因默认情况下未将其激活。SCM 的全部意义在于它不是一次性事件,而是连续的历史记录。从根本上讲,这也意味着成功的攻击需要随着时间的流逝而发展,并且不可检测。
如果您可以欺骗一次SCM,插入您的代码,然后在下周检测到它,则实际上您没有做任何有用的事情。你只是烧死自己。
乔伊·赫斯(Joey Hess)在Git仓库中尝试使用这些pdf文件,他发现:
其中包括两个具有相同SHA和大小的文件,由于git将标头添加到内容的方式,它们的斑点确实不同。
joey@darkstar:~/tmp/supercollider>sha1sum bad.pdf good.pdf
d00bbe65d80f6d53d5c15da7c6b4f0a655c5a86a bad.pdf
d00bbe65d80f6d53d5c15da7c6b4f0a655c5a86a good.pdf
joey@darkstar:~/tmp/supercollider>git ls-tree HEAD
100644 blob ca44e9913faf08d625346205e228e2265dd12b65 bad.pdf
100644 blob 5f90b67523865ad5b1391cb4a1c010d541c816c1 good.pdf
虽然将相同的数据附加到这些冲突文件中确实会产生其他冲突,但前置数据则不会。
- 生成一个常规的提交对象;
- 使用整个提交对象+ NUL作为所选前缀,并且
- 使用相同前缀的碰撞攻击来生成碰撞的好/坏对象。
- ...这是没有用的,因为好的和坏的提交对象仍然指向同一棵树!
另外,您已经可以通过以下方式检测到针对每个文件中存在的SHA-1的密码分析冲突攻击: cr-marcstevens/sha1collisiondetection
在Git本身中添加类似的检查会带来一定的计算成本。
关于更改哈希,Linux注释:
散列的大小和散列算法的选择是独立的问题。
您可能要做的是切换到256位哈希,在内部和本机git数据库中使用该哈希,然后默认情况下仅 将哈希显示为40个字符的十六进制字符串(有点像我们已经在其中将其缩写)很多情况)。
这样,除非传入一些特殊的“--full-hash
”参数(或“--abbrev=64
”之类的东西,否则默认情况下,git的工具甚至看不到更改),默认是我们缩写为40)。
但是,过渡计划(从SHA1到另一个哈希函数)的过渡计划仍然很复杂,但仍在积极研究中。
一个convert-to-object_id
活动是正在进行中:
3月20日更新:GitHub详细介绍了可能的攻击及其保护措施:
可以通过各种机制为SHA-1名称分配信任。例如,Git允许您对提交或标签进行加密签名。这样做仅对提交或标记对象本身进行签名,而使用它们的SHA-1名称依次指向包含实际文件数据的其他对象。这些对象发生冲突可能会产生一个看起来有效的签名,但该签名所指向的数据与签名者预期的不同。在这种攻击中,签名者仅看到碰撞的一半,而受害者则看到了另一半。
保护:
最近的攻击使用特殊技术来利用SHA-1算法中的弱点,这些弱点可以在更短的时间内找到冲突。这些技术在字节中留下了一个模式,当计算冲突对中任一对的SHA-1时可以检测到。
GitHub.com现在对它计算的每个SHA-1执行此检测,如果有证据表明该对象是碰撞对的一半,则中止操作。这样可以防止攻击者使用GitHub诱使项目接受其冲突的“无辜”部分,并阻止他们托管恶意部分。
参见马克·史蒂文斯sha1collisiondetection
(Marc Stevens)的 “ ”
再次,随着2018年第一季度Git 2.16添加了表示哈希算法的结构,开始向新哈希过渡的实现。
如上所述,新的受支持的哈希将为SHA-256。
git-svn
尽管“虽然确实有一些问题”背后的链接是间接提及的)
我认为密码学家会庆祝。
2005年2月,宣布了王晓云,尹奕群和于洪波的攻击。攻击可以在完整版本的SHA-1中发现冲突,所需操作少于2 ^ 69。(强行搜索将需要2 ^ 80次操作。)
y
,使得h(x) ==
H(Y)`这对于像SSL证书任意数据严重威胁然而,这并不影响混帐这将是脆弱的第二原像攻击这意味着,有消息x
你可以修改它的消息x'
说h(x) == h(x')
。因此,这种攻击不会削弱Git。而且出于安全原因,Git尚未选择SHA-1。
对于SHA-1这样的哈希,有几种不同的攻击模型,但是通常讨论的一种是冲突搜索,包括Marc Stevens的HashClash工具。
“截至2012年,对SHA-1的最有效攻击被Marc Stevens [34]认为,通过从云服务器租用CPU功能来破坏单个哈希值的估计费用为277万美元。”
正如人们指出的那样,您可以使用git强制进行哈希冲突,但这样做不会覆盖另一个存储库中的现有对象。我以为甚至git push -f --no-thin
不会覆盖现有的对象,但不能100%确定。
就是说,如果您入侵远程存储库,则可以使假对象成为那里的旧对象,可能将被入侵的代码嵌入到github或类似的开源项目中。如果您小心的话,也许可以介绍一个新用户下载的黑客版本。
但是我怀疑该项目的开发人员可能会做很多事情,从而暴露或意外破坏您的数百万美元黑客。特别是,如果某个您没有黑客的开发人员git push --no-thin
在修改了受影响的文件之后运行了上述程序,这甚至是一笔巨款,有时甚至没有--no-thin
依赖。