碰撞的可能性

Likelihood of Collision

我想散列一个内部帐号并将结果用作帐户记录的唯一 public 标识符。标识符限制为 40 个字符。我有大约 250 条带有唯一帐号的记录。

发生碰撞的可能性较小。

  1. 对帐号的 SHA-256 哈希值进行 SHA-1。
  2. 取账号的SHA-256,挑出40个字符。

这些方法是相同的 (*),因此您应该使用第二种方法。没有理由将 SHA-1 注入系统。 SHA-256 中的任何 select 位都是独立的并且“有效地随机”。

另一个可能方便的替代解决方案是 。如果您对自己的名字space保密(这是允许的),这可能是完成您所描述的事情的一种非常好的方式。

(*) 关于您在此处使用“字符”而不是字节这一事实存在一些微妙之处,并且您可以通过使用比您更好的编码在 40 个“字符”中获得更大的 space可能正在使用。根据您实际编码的方式,space 可能会略有不同。但这并不重要。这些 space 是巨大的,并且这两种方法在实践中将是相同的,所以使用只需要一种算法的方法。


另一种可能更好地满足您的需求的方法是扩展标识符。如果 space 足够稀疏(即,如果可能的标识符数量远远大于实际使用的标识符数量),那么像 PBKDF2 这样的拉伸算法就是专门为处理这个问题而设计的。它们的计算成本很高,但您可以调整它们的成本以满足您的安全要求。

散列的一般问题是散列非常快,如果您的 space 可能的标识符非常少,那么很容易暴力破解。拉伸算法使任意猜测的成本变得非常昂贵,因此大 spaces 对于蛮力来说是不切实际的。他们这样做不需要任何秘密,这很好。一般做法是:

  • Select 一个“盐”值。这是可以公开的。不要紧。对于这个特定的用例,因为每个帐号都不同,所以您可以 select 一个全局盐。 (如果受保护的数据可能相同,那么对每条记录使用不同的盐很重要。)
  • 计算PBKDF2(salt, iterations, length, payload)

迭代次数调整此操作的速度。输出是“有效随机的”(就像哈希一样)并且可以以相同的方式使用。

迭代的一个共同目标是提供大约 80-100 毫秒的值。这在服务器上相当快,但对于 brute-forcing 大 spaces 来说非常慢,即使攻击者拥有比你更好的硬件。理想情况下,您的 space 应该至少需要数百万年的暴力破解(说真的;这是我们在安全方面通常喜欢的那种净空;我个人的目标是数万亿年)。如果它小于几年,那么它可能可以通过向它投入更多硬件来快速暴力破解。

(当然,所有这些选择都可以根据您的攻击模型进行调整。这取决于您对攻击的专注程度和 well-funded 预期。)

一个 40 个字符的 ID 是 320 位,这给了你很多 space。只有 250 条记录,您可以轻松地将一个独特的计数器放入其中。三位数只有 24 位,您可以使用 000 到 999 的范围。例如,用部分 SHA-256 哈希的十六进制表达式填充 ID 的其余部分。使用 3 位 ID,剩下 37 个位置用于十六进制,涵盖 Sha-256 输出的 37*4 = 148 位。

您可能希望将计数器放在十六进制字符串中间的固定位置,而不是放在开头或结尾,以使其不那么明显。

<11 hex chars><3 digit ID><26 hex chars>