这可能永远不会发生在现实世界中,也许永远不会发生,但让我们考虑一下:假设你有一个git存储库,做一个提交,并且变得非常不幸:其中一个blob最终拥有相同的SHA-1作为另一个已存在于您的存储库中。问题是,Git将如何处理这个?简直失败了?找到一种方法来链接两个blob并根据上下文检查哪一个需要?
更多的是脑筋急转弯而非实际问题,但我发现这个问题很有趣。
答案 0 :(得分:677)
我做了一个实验,以确切了解Git在这种情况下的行为方式。这是版本2.7.9~rc0 + next.20151210(Debian版本)。我基本上只是通过应用以下差异和重建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都可以,因为它会因错误而停止。我认为如果它至少在所有情况下都失败了会更好。
答案 1 :(得分:237)
原始答案(2012)(参见下面shattered.io
2017 SHA1碰撞)
old (2006) answer from Linus可能仍然相关:
不。如果它具有相同的SHA1,则意味着当我们从另一端收到对象时,我们将不覆盖我们已有的对象。
所以会发生的是,如果我们看到碰撞,那么早先&#34;任何特定存储库中的对象总是会覆盖。但请注意&#34;早期&#34;显然是每个存储库,在某种意义上,git对象网络生成的DAG不是完全有序的,所以不同的存储库会同意什么是早期&#34;在直接祖先的情况下,如果对象来自单独的而不是直接相关的分支,则两个不同的回购可能显然已经以不同的顺序获得这两个对象。
但是,之前的&#34;会覆盖&#34;从安全的角度来看,你非常想要的是:记住git模型是你应该主要只信任你的自己的存储库。
因此,如果您执行&#34;git pull
&#34;,根据定义,新传入对象的可信度低于您已有的对象,因此允许新对象是错误的 替换旧的。所以你有两种碰撞案例:
无意中,你不知何故非常不走运,两个文件最终拥有相同的SHA1。
此时,当您提交该文件(或执行&#34;git-update-index
&#34;将其移入索引但尚未提交)时,会发生新内容的SHA1被计算,但是因为它匹配旧对象,所以不会创建新对象,并且commit-or-index最终指向旧对象 。
您不会立即注意到(因为索引将与旧对象SHA1匹配,这意味着类似&#34;git diff
&#34;会使用签出的副本),但如果您你做过树级别的差异(或者你做克隆或拉动,或者强行结账)你会突然注意到该文件已经变成完全与你预期的不同。<登记/> 所以你通常会很快注意到这种碰撞 在相关新闻中,问题是如何处理无意碰撞..
首先,让我提醒人们,无意中的碰撞实际上真的不太可能,所以我们很可能永远不会在宇宙的整个历史中看到它。
但如果它发生了,它不是世界末日:你最有可能做的只是改变稍微相撞的文件,而只是强迫带有更改内容的新提交(添加评论说&#34;/* This line added to avoid collision */
&#34;)然后教git关于已被证明是危险的魔法SHA1。
因此,在过去的几百万年中,我们可能需要添加一两个“中毒”#34; SHA1值为git。它不太可能成为维护问题;)攻击者发生碰撞,因为有人破坏(或暴力强迫)SHA1。 这个显然比无意的更容易很多,但根据定义,它总是一个&#34;远程&#34;库。如果攻击者可以访问本地存储库,那么他就有更容易的方法来搞砸你 所以在这种情况下,碰撞完全不是问题:你会得到一个&#34;坏&#34;存储库与攻击者的意图不同,但是因为您从未实际使用过他的碰撞对象,所以 字面上与攻击者没有找到完全碰撞,但只使用你已经拥有的对象(即它100%等同于&#34;平凡&#34;生成相同SHA1的相同文件的碰撞)。
question of using SHA-256经常被提及,但暂时没有采取行动(2012) 注意:starting 2018 and Git 2.19,代码被重构为使用SHA-256。
注意(幽默):您可以使用gitbrute中的项目Brad Fitzpatrick (bradfitz
)强制提交特定的SHA1 前缀。
gitbrute强制执行一对作者+提交者时间戳,以便生成的git commit具有您想要的前缀。
示例:https://github.com/bradfitz/deadbeef
Daniel Dinnyes指出in the comments到7.1 Git Tools - Revision Selection,其中包括:
在同一天晚上,你的编程团队的每个成员都会被无关紧要的事件中的狼群攻击并杀死。
即使是最近(2017年2月)shattered.io
也证明了伪造SHA1碰撞的可能性:
(请参阅 separate answer 中的更多内容,包括Linus Torvalds&#39; Google+帖子)
参见&#34; Lifetimes of cryptographic hash functions&#34;来自 Valerie Anita Aurora 了解更多信息 在那页中,她注意到:
Google花了6500个CPU年和110年GPU来说服每个人我们需要停止使用SHA-1来处理安全关键应用程序。
还因为它很酷
在我的 separate answer below 中查看更多内容。
答案 2 :(得分:41)
根据Pro Git:
如果您确实提交的哈希值与存储库中的上一个对象具有相同的SHA-1值,Git将会在Git数据库中看到前一个对象并假设它已经被写入。如果您尝试在某个时刻再次检出该对象,您将始终获得第一个对象的数据。
所以它不会失败,但它也不会保存你的新物体。
我不知道命令行会怎么样,但这肯定会令人困惑。
再往下一点,同样的参考试图说明这种碰撞的可能性:
这是一个示例,让您了解如何获得SHA-1冲突。如果地球上所有65亿人都在进行编程,而每一秒,每一个人都生成的代码相当于整个Linux内核历史(100万Git对象)并将其推入一个巨大的Git存储库,则需要5年时间该存储库包含足够的对象,以使单个SHA-1对象发生碰撞的概率为50%。你的编程团队的每个成员都会在同一天晚上被无关紧要的事件中的狼群攻击并杀死。
答案 3 :(得分:22)
要添加到my previous answer from 2012,现在(2017年2月,五年后),与shattered.io实际SHA-1碰撞的示例,您可以在其中制作两个碰撞的PDF文件:在第一个PDF文件上获取SHA-1数字签名,该签名也可能被滥用为第二个PDF文件上的有效签名。
另请参阅&#34; At death’s door for years, widely used SHA1 function is now dead&#34;和this illustration。
2月26日更新:Linus确认了以下几点in a Google+ post:
(1)首先 - 天空不会下降。使用加密哈希来进行安全签名,并使用一个用于生成内容标识符&#34;之间的差别很大。对于像git这样的内容可寻址系统。
(2)其次,这种特殊SHA1攻击的性质意味着它实际上很容易被缓解,而且已经发布了两套针对该缓解的补丁。
(3)最后,实际上是一个相当直接的过渡到其他一些不会打破世界的哈希 - 甚至是旧的git存储库。
关于该转换,请参阅Q1 2018 Git 2.16添加表示哈希算法的结构。该过渡的实施已经开始。
Starting Git 2.19 (Q3 2018),Git已选择 SHA-256作为NewHash ,并且正在将其集成到代码中(意味着SHA1仍然是默认值(Q2 2019,Git 2.21) ,但SHA2将是继任者)
原始答案(2月25日) 但是:
git-svn
though。或者更确切地说with svn itself,seen here。git fsck
轻松检测到,如今mentioned by Linus Torvalds。 git fsck
会警告NUL
之后隐藏不透明数据的提交消息(尽管NUL
isn't always present in a fraudulent file)。
不是每个人都打开transfer.fsck
,但是GitHub会这样做:如果出现格式错误的对象或链接断开,任何推送都会中止。虽然......有reason this is not activated by default。SCM的整个点是它不是一次性事件, 但关于连续的历史。这也从根本上意味着一个 成功的攻击需要随着时间的推移而发挥作用,而且无法被发现 如果您可以欺骗SCM一次,请插入您的代码,然后就可以了 在下周检测到,你实际上并没有做任何有用的事情。只有你 烧了自己。
Joey Hess在a Git repo和he found中尝试这些pdf:
包括两个具有相同SHA和大小的文件,它们确实得到了 不同的blob感谢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
虽然会生成将相同数据附加到这些冲突文件 其他冲突,前置数据不会。
所以main vector of attack (forging a commit) would be:
- 生成常规提交对象;
- 使用整个提交对象+ NUL作为所选前缀,并使用
- 使用相同前缀冲突攻击来生成冲突的好/坏对象。
- ...这没用,因为好的和坏的提交对象仍指向同一棵树!
另外,您已经可以使用cr-marcstevens/sha1collisiondetection
检测每个文件中存在的SHA-1的密码分析冲突攻击
在Git中添加类似的检查would have some computation cost。
在更改哈希时,Linux comments:
散列的大小和散列算法的选择是独立的问题 您可能要做的是切换到256位哈希,使用它 在内部和本机git数据库中,然后默认情况下 将哈希显示为40个字符的十六进制字符串(有点像我们如何 在许多情况下已经缩写了。) 就这样,git的工具甚至不会看到变化,除非传入 一些特别的&#34;
--full-hash
&#34;论证(或&#34;--abbrev=64
&#34;或其他 - 默认是我们缩写为40)。
仍然是transition plan (from SHA1 to another hash function) would still be complex,但积极研究
A convert-to-object_id
campaign是in progress:
3月20日更新:GitHub detail a possible attack and its protection:
可以通过各种机制为SHA-1名称分配信任。例如,Git允许您以加密方式对提交或标记进行签名。这样做只会签署提交或标记对象本身,而后者又通过使用SHA-1名称指向包含实际文件数据的其他对象。这些对象中的冲突可以产生看似有效的签名,但是指向与签名者意图不同的数据。在这样的攻击中,签名者只能看到一半的碰撞,受害者看到另一半。
保护:
最近的攻击使用特殊技术来利用SHA-1算法中的弱点,在较短的时间内发现冲突。这些技术在字节中留下了一个模式,在计算碰撞对的一半的SHA-1时可以检测到这种模式。
GitHub.com现在对它计算的每个SHA-1执行此检测,如果有证据表明该对象是碰撞对的一半,则中止该操作。这可以防止攻击者使用GitHub来说服项目接受“无辜的”#34;他们碰撞的一半,以及阻止他们托管恶意的一半。
参见&#34; sha1collisiondetection
&#34; Marc Stevens
同样,在Q1 2018 Git 2.16添加表示散列算法的结构的情况下,已开始实现向新散列的转换。
如上所述,新支持的Hash将为SHA-256。
答案 4 :(得分:6)
我认为密码学家会庆祝。
2005年2月,王晓云,益群丽莎和洪波宇的一次袭击被宣布。 攻击可以在完整版SHA-1中发现冲突,只需要少于2 ^ 69次操作。 (蛮力搜索需要2 ^ 80次操作。)
答案 5 :(得分:5)
对于像SHA-1这样的哈希有几种不同的攻击模型,但通常讨论的是冲突搜索,包括Marc Stevens的HashClash工具。
正如大家所指出的,你可以强制与git进行哈希冲突,但这样做不会覆盖另一个存储库中的现有对象。我想,即使git push -f --no-thin
也不会覆盖现有的对象,但不是100%肯定。
也就是说,如果您入侵远程存储库,那么您可以将您的虚假对象变为旧的那里,可能将被黑客入侵的代码嵌入到github或类似的开源项目中。如果你小心,那么也许你可以介绍新用户下载的黑客版本。
然而,我怀疑项目开发人员可能做的很多事情可能会暴露或意外地破坏你的数百万美元的黑客攻击。特别是,如果一些开发人员(你没有破解)在修改受影响的文件后运行前面提到的git push --no-thin
,有时即使没有--no-thin
依赖,也会耗费大量资金。