【问题标题】:Handling close-to-impossible collisions on should-be-unique values在应该是唯一的值上处理几乎不可能的冲突
【发布时间】:2012-02-10 05:48:52
【问题描述】:

有许多系统依赖于某些特定值的唯一性。想到任何使用 GUID 的东西(例如 Windows 注册表或其他数据库),还有从对象创建哈希以识别它并因此需要此哈希唯一的东西。

散列表通常不介意两个对象是否具有相同的散列,因为散列仅用于将对象分解为类别,因此在查找时,不是表中的所有对象,而是表中的那些对象必须将同一类别(存储桶)与搜索对象进行身份比较。

但是(似乎)其他实现取决于唯一性。我的例子(这就是我提出这个问题的原因)是 Mercurial 的修订 ID。 Mercurial 邮件列表中的entry 正确声明

变更集哈希的几率 第一次意外相撞 十亿次提交基本上为零。但 我们会注意到它是否发生。和 你会因为那个人而出名 意外破坏了 SHA1。

但即使是最小的概率也不意味着不可能。现在,我不想解释为什么完全可以依赖唯一性(例如,已经讨论过here)。这一点我很清楚。

相反,我想知道(也许通过你自己工作中的例子):

  • 是否有任何最佳实践来涵盖这些不太可能的情况?

  • 是否应该忽略它们,因为特别强烈的太阳风更有可能导致硬盘读取错误?

  • 是否应该至少对其进行测试,如果只是失败并向用户显示“我放弃,你已经完成了不可能”的消息?

  • 或者即使这些情况也应该得到妥善处理?

对我来说,尤其是以下内容很有趣,尽管它们有些敏感:

  • 如果你不处理这些情况,你会如何对抗不听概率的直觉?

  • 如果你确实处理了它们,你如何证明这项工作的合理性(对你自己和他人),考虑到有更多可能的情况你不处理,比如超新星?

【问题讨论】:

  • 如果开发人员包含了一些测试代码,最近的OpenSSL vulnerability 可能会更早被检测到。显然,它不应该尝试遍历所有可能的来源,但是如果它在没有警告的情况下运行一百万次迭代,您会很好地了解可能性。知识胜于信仰。
  • 例如,这里是一个示例,其中 MSFT support.microsoft.com/kb/304017">checking 用于 GUID 空间中的冲突 导致 SQL Server 中的错误必须在 Windows 2000 中进行修补.
  • 还有一个非零的概率你通过你的椅子做量子隧道并跌倒在地板上,但在下面放一个枕头是矫枉过正的。这在很大程度上取决于你在做什么。如果您正在开发隧道显微镜,那么您想要处理的是意想不到的和不可能的事情(特别是因为在那个规模上它变得不可忽视)。从技术上讲,内存不足的情况比 SHA 冲突更有可能,但我从未见过认真处理 OOM 的代码。

标签: language-agnostic unique


【解决方案1】:
  • 如果您确实处理了它们,您如何证明这项工作的合理性(对您自己和其他人),考虑到您不处理更多可能的情况,例如超新星?

对此的答案是,您不是在测试以发现偶然发生的 GUID 冲突。您正在测试发现由于 GUID 代码中的错误或 GUID 代码依赖于您已违反(或被某些攻击者欺骗违反)的前提条件而发生的 GUID 冲突,例如在 V1 中 MAC地址是唯一的,时间会向前发展。两者都比基于超新星的错误更有可能。

但是,并非 GUID 代码的每个客户端都应测试其正确性,尤其是在生产代码中。这就是单元测试应该做的事情,所以要权衡错过一个您实际使用会捕获但单元测试没有捕获的错误的成本,而不是一直对您的库进行事后猜测的成本。

另请注意,GUID 只有在生成它们的每个人都合作时才有效。如果您的应用程序在您控制的机器上生成 ID,那么您可能无论如何都不需要 GUID - 像递增计数器这样的本地唯一 ID 可能会很好。显然 Mercurial 不能使用它,因此它使用散列,但最终 SHA-1 将陷入产生冲突(或者更糟糕的是,前映像)的攻击,并且它们将不得不改变。

如果您的应用在您无法控制的机器(例如客户端)上生成非散列“GUID”,那么您就不必担心意外冲突,您会担心恶意客户端试图对您的服务器执行 DOS 时的故意冲突。无论如何,保护自己免受伤害可能会保护您免受意外伤害。

  • 或者即使这些情况也应该得到妥善处理?

这个问题的答案可能是“不”。如果您可以像哈希表一样优雅地处理冲突的 GUID,那么为什么还要为 GUID 烦恼呢? “标识符”的全部意义在于,如果两个事物具有相同的 ID,那么它们就是相同的。如果您不想对它们一视同仁,只需像哈希表一样将它们最初引导到存储桶中,然后使用不同的方案(如哈希)。

【讨论】:

  • +1 有趣的是,我什至没有将错误视为碰撞的原因。
  • MAC 地址也不总是唯一的,在某些情况下,一堆廉价仿冒品具有相同的 MAC 地址。
  • +1 在绝大多数情况下,128 位哈希上的冲突比意外冲突更可能是错误或攻击。
  • @Brad :我听说硬件允许更改 MAC 地址。在任何情况下,您都可以使用任何 MAC 配置虚拟接口。如果您正在运行一堆虚拟机,无论出于何种虐待狂的原因,所有虚拟机都具有相同的接口设置,那么您将通过不同机器上的相同 MAC 地址播种。
  • 很好的分析,但我不同意你的结论。 4 位用于 GUID 版本字段,2 位用于变体字段,因此您实际上只有 122 位。对于 2^122,GUID 冲突的可能性似乎仍然很低,除非您考虑到大量用户(例如 500,000,000 个 Facebook 用户或 1B 个 Google 用户)可能正在执行导致以一定速度创建新 GUID 的操作每秒 1-2 次。
【解决方案2】:

给定一个良好的 128 位散列,在给定随机输入的情况下与特定散列值发生冲突的可能性是:

1 / 2 ** 128 大约等于3 * 10 ** -39

可以使用用于解释birthday problem 的逻辑来计算给定n 样本没有发生冲突(p) 的概率。

p = (2 ** 128)! / (2 ** (128 * n) * (2 ** 128 - n)!)

其中!表示阶乘函数。随着样本数量的增加,我们可以绘制没有碰撞的概率:

Probability of a random SHA-1 collision as the number of samples increases. http://img21.imageshack.us/img21/9186/sha1collision.png

10**1710**18 哈希之间,我们开始看到从 0.001% 到 0.14% 的不平凡的冲突可能性,最后是 13% 与 10**19 哈希。因此,在一个拥有一百万、十亿个记录的系统中,依靠唯一性记录可能是不明智的(这样的系统是可以想象的),但在绝大多数系统中,发生冲突的可能性非常小,以至于您可以依赖哈希的唯一性出于所有实际目的。

现在,抛开理论不谈,碰撞更有可能通过错误或有人攻击您的系统被引入您的系统,因此即使意外碰撞的可能性几乎为零,每个人的回答都提供了检查碰撞的充分理由小(也就是说bug或恶意的概率远高于意外碰撞)。

【讨论】:

    猜你喜欢
    • 2015-04-22
    • 1970-01-01
    • 1970-01-01
    • 2021-05-25
    • 1970-01-01
    • 1970-01-01
    • 2011-11-10
    • 2011-10-31
    • 2014-03-23
    相关资源
    最近更新 更多