26

PHP 有一个uniqid()函数,可以生成各种 UUID。

在使用示例中,它显示以下内容:

$token = md5(uniqid());

但在评论中,有人这样说

从唯一 ID 生成 MD5 是幼稚的,并且会降低唯一 ID 的大部分价值,并在 MD5 域上提供显着(可攻击)的限制。这是一件非常破碎的事情。正确的做法是单独使用唯一ID;它已经为非碰撞做好了准备。

如果是这样,为什么这是真的?如果 MD5 散列(几乎)对于唯一 ID 来说是唯一的,那么 md5'ing uniqid 有什么问题?

4

6 回答 6

45

UUID 为 128 位宽,并且具有其生成方式所固有的唯一性。MD5 散列是 128 位宽,不保证唯一性,只保证低概率的冲突。MD5 哈希不小于 UUID,因此它对存储没有帮助。

如果您知道哈希来自 UUID,那么攻击会容易得多,因为如果您知道有关生成它们的机器的任何信息,那么有效 UUID 的域实际上是相当可预测的。

如果您需要提供安全令牌,则需要使用加密安全随机数生成器。(1) UUID 并非设计为加密安全,仅保证唯一。由唯一机器标识符(通常是 MAC)和时间限制的单调递增序列仍然是完全有效的 UUID,但如果您可以从令牌序列中逆向工程单个 UUID,则具有高度可预测性。

  1. 加密安全 PRNG 的定义特征是给定迭代的结果不包含足够的信息来推断下一次迭代的值 - 即,生成器中有一些隐藏状态没有在数字中显示并且无法推断通过检查来自 PRNG 的一系列数字。

    如果您进入数论,您可以找到方法从一系列生成的值中猜测某些 PRNG 的内部状态。 梅森捻线机就是这种生成器的一个例子。它具有隐藏状态,它曾经获得很长一段时间,但它在密码学上并不安全 - 您可以采用相当小的数字序列并使用它来推断内部状态。完成此操作后,您可以使用它来攻击依赖于对该序列保密的加密机制。
于 2009-08-18T13:19:14.287 回答
14

请注意,uniqid()它不会返回UUID,而是基于当前时间的“唯一”字符串:

$ php -r 'echo uniqid("prefix_", true);'
prefix_4a8aaada61b0f0.86531181

如果你多次这样做,你会得到非常相似的输出字符串,熟悉的每个人uniqid()都会认出源算法。这样就很容易预测将要生成的下一个 ID。

md5() 输出的优势,以及特定于应用程序的盐字符串或随机数,是一种更难猜测字符串的方法:

$ php -r 'echo md5(uniqid("prefix_", true));'
3dbb5221b203888fc0f41f5ef960f51b

与 plain 不同uniqid(),这每微秒产生非常不同的输出。此外,它不会透露您的“前缀盐”字符串,也不会透露您在幕后使用uniqid()。在不知道盐的情况下,很难(认为不可能)猜测下一个 ID。

总之,我不同意评论者的观点,并且总是更喜欢md5()-ed 输出而不是 plain uniqid()

于 2009-08-18T13:30:15.510 回答
5

对 UUID 进行 MD5 处理毫无意义,因为 UUID 已经是唯一且固定长度(短)的,这些属性是人们经常使用 MD5 开始的一些原因。所以我想这取决于你打算用 UUID 做什么,但一般来说,UUID 与一些经过 MD5 处理的数据具有相同的属性,那么为什么两者都做呢?

于 2009-08-18T13:16:08.500 回答
2

UUID 已经是唯一的,因此无论如何对它们进行 MD5 处理毫无意义。

关于安全问题,一般来说,如果攻击者可以预测你将要生成的下一个唯一 ID 是什么,你就会受到攻击。如果已知您从 UUID 生成唯一 ID,则潜在的下一个唯一 ID 的集合要小得多,从而为暴力攻击提供更好的机会。

如果攻击者可以从您那里获得一大堆唯一 ID,并且通过这种方式猜测您生成 UUID 的方案,则尤其如此。

于 2009-08-18T13:20:12.820 回答
2

UUID 的第 3 版已经是 MD5 了,所以没有必要再做一次。但是,我不确定 PHP 使用什么 UUID 版本。

于 2009-08-18T13:23:00.680 回答
0

顺便说一句,MD5 实际上已经过时,并且从 2010 年起不再用于任何值得保护的东西——PHI、PII 或 PCI。美国联邦调查局已强制执行此规定,任何不合规的实体都将支付大量美元的罚款。

于 2009-08-18T14:21:18.420 回答