如何评估哈希冲突概率?

Posted

技术标签:

【中文标题】如何评估哈希冲突概率?【英文标题】:How do I assess the hash collision probability? 【发布时间】:2010-10-26 03:06:12 【问题描述】:

我正在为搜索系统开发一个后端应用程序。搜索系统将文件复制到一个临时目录并给它们随机命名。然后它将临时文件的名称传递给我的应用程序。我的应用程序必须在有限的时间内处理每个文件,否则它会被关闭——这是一种类似看门狗的安全措施。处理文件可能需要很长时间,因此我需要设计能够处理这种情况的应用程序。如果我的应用程序在下次搜索系统想要索引同一个文件时关闭,它可能会给它一个不同的临时名称。

显而易见的解决方案是在搜索系统和后端之间提供一个中间层。它将请求排队到后端并等待结果到达。如果请求在中间层超时 - 没问题,后端将继续工作,只有中间层重新启动,当搜索系统稍后重复请求时,它可以从后端检索结果。

问题是如何识别文件。他们的名字随机变化。我打算使用像 MD5 这样的散列函数来散列文件内容。我很清楚birthday paradox 并使用链接文章中的估计来计算概率。如果我假设我的文件不超过 100,000 个,则两个文件具有相同 MD5(128 位)的概率约为 1,47x10-29

我应该关心这种冲突概率还是只是假设相等的哈希值意味着相等的文件内容?

【问题讨论】:

这是文件名内容的哈希值吗? 内容经过哈希处理。散列文件名没有意义 - 它们随机变化。 如果您担心冲突,请同时考虑文件大小和哈希值。 【参考方案1】:

相等的哈希意味着相等的文件,除非有人恶意破坏您的文件并注入冲突。 (如果他们从互联网上下载东西,可能就是这种情况)如果是这种情况,请使用基于 SHA2 的功能。

没有意外的 MD5 碰撞,1,47x10-29 是一个非常非常非常小的数字。

为了克服重新散列大文件的问题,我将采用 3 阶段身份方案。

    仅文件大小 文件大小 + 文件中不同位置的 64K * 4 哈希 一个完整的哈希

因此,如果您看到一个具有新大小的文件,您肯定知道您没有重复文件。等等。

【讨论】:

@sharptooth 查看这个问题,了解一些可以使用的技巧:***.com/questions/788761/… 在 DB 中已有 25K 图像后,我遇到了第一次 MD5 冲突 “没有意外的 MD5 冲突”——这句话是错误的。 喜欢在散列整个文件之前散列一些小部分的想法。可以大大加快重复文件检查。【参考方案2】:

仅仅因为概率是 1/X 并不意味着在您有 X 条记录之前它不会发生在您身上。就像彩票一样,你不太可能中奖,但有人中奖。

以当今计算机的速度和容量(甚至不谈论安全性,只谈论可靠性),真的没有理由不使用比 MD5 更大/更好的哈希函数来处理任何关键问题。升级到 SHA-1 应该可以帮助您在晚上睡得更好,但如果您想格外小心,请转到 SHA-265,不要再考虑它了。

如果性能确实是个问题,那么请使用实际上比 MD5 更快但支持 256+ 位的 BLAKE2,以在具有相同或更好性能的同时降低冲突的可能性。然而,虽然 BLAKE2 已被广泛采用,但它可能需要向您的项目添加新的依赖项。

【讨论】:

不过,有了彩票,您就有了中奖的保证。鉴于没有已知的 SHA256 冲突,从技术上讲,在完全耗尽之前永远不会发生冲突,对吧? 好点,通常对于文件哈希应用程序,您可以非常安全地假设 SHA-256 永远不会产生冲突(与 git 使用的 SHA1 不同,并且在大型现实世界中发生了冲突项目)。但是,如果使用 SHA-256 对随机输入位进行散列(例如生成会话 id),您仍然应该考虑到对于给定数量的输入位,RNG 冲突的可能性是相同的,而不管使用的散列方法如何。也就是说,使用 SHA-256 散列一个随机的 32 位整数仍然只是 32 位数据,因此可能会发生冲突。【参考方案3】:

我认为你不应该。

但是,如果您有两个相同文件具有不同(真实名称,而不是基于 md5)的概念,您应该这样做。例如,在搜索系统中,两个文档可能具有完全相同的内容,但由于它们位于不同的位置而不同。

【讨论】:

这是搜索系统的问题,而不是我的应用程序的问题。我的应用程序只需要从传递的文件中提取文本。【参考方案4】:

我想出了一种蒙特卡罗方法,以便在将 UUID 用于必须序列化而不会发生冲突的分布式系统时能够安全地休眠。

from random import randint
from math import log
from collections import Counter

def colltest(exp):
    uniques = []
    while True:
        r = randint(0,2**exp)
        if r in uniques:
            return log(len(uniques) + 1, 2)
        uniques.append(r)

for k,v in Counter([colltest(20) for i in xrange(1000)]):
    print k, "hash orders of magnitude events before collission:",v

会打印出类似的东西:

5 hash orders of magnitude events before collission: 1
6 hash orders of magnitude events before collission: 5
7 hash orders of magnitude events before collission: 21
8 hash orders of magnitude events before collission: 91
9 hash orders of magnitude events before collission: 274
10 hash orders of magnitude events before collission: 469
11 hash orders of magnitude events before collission: 138
12 hash orders of magnitude events before collission: 1

我之前听说过这个公式:如果需要存储 log(x/2) 键,请使用至少具有键空间 e**(x) 的散列函数。

重复实验表明,对于 1000 个 log-20 空间的群体,有时早在 log(x/4) 时就会发生碰撞。

对于 122 位的 uuid4,这意味着我可以安全地睡觉,而几台计算机会随机选择 uuid,直到我有大约 2**31 个项目。我正在考虑的系统中的峰值事务大约是每秒 10-20 个事件,我假设平均为 7 个。考虑到这种极端的偏执,这给了我大约 10 年的操作窗口。

【讨论】:

【参考方案5】:

这是一个交互式计算器,可让您估计任何散列大小和对象数量的冲突概率 - http://everydayinternetstuff.com/2015/04/hash-collision-probability-calculator/

【讨论】:

问题不在于估计概率。我知道概率。问题是我接下来要做什么。 接下来要做的事情很简单:你选择一个比特数更多,最好分布更好的哈希函数,比如sha1,然后描述发生冲突的可能性,发生时会发生什么,以及发生什么是后果。

以上是关于如何评估哈希冲突概率?的主要内容,如果未能解决你的问题,请参考以下文章

真的太重要了,面试出现的概率达到了 99%!!!对于哈希表的知识(建议收藏)

哈希表原理及如何避免键值冲突法?

iOS中的哈希表

erlang node_name phash 冲突坑

字符串哈希

哈希表之三冲突解决