Random 随机令牌生成-发生了不太可能的冲突

Random 随机令牌生成-发生了不太可能的冲突,random,token,uuid,birthday-paradox,Random,Token,Uuid,Birthday Paradox,几个月前,我们使用UUID生成随机字符串ID,这些ID需要在所有方面都是唯一的。然后我更改了算法,以便在数据库中保存一些数据和索引空间。我测试了几种生成唯一字符串ID的方法,并决定使用此函数: function generateToken($length) { $characters = '0123456789abcdefghijklmnopqrstuvwxyz'; $max = strlen($characters) - 1; $token = ''; for

几个月前,我们使用UUID生成随机字符串ID,这些ID需要在所有方面都是唯一的。然后我更改了算法,以便在数据库中保存一些数据和索引空间。我测试了几种生成唯一字符串ID的方法,并决定使用此函数:

function generateToken($length) {
    $characters = '0123456789abcdefghijklmnopqrstuvwxyz';
    $max = strlen($characters) - 1;

    $token = '';
    for ($i = 0; $i < $length; $i++) {
        $token .= $characters[mt_rand(0, $max)];
    }

    return $token;
}
函数generateToken($length){
$characters='0123456789abcdefghijklmnopqrstuvwxyz';
$max=strlen($characters)-1;
$token='';
对于($i=0;$i<$length;$i++){
$token.=$characters[mt_rand(0,$max)];
}
返回$token;
}
我使用这个函数来生成20个字符长的ID,使用数字和字母,或者你可以说这些ID是以36为基数的数字。任何两个ID碰撞的概率应为1/36^20,但由于生日悖论,预计碰撞发生在大约36^10个记录之后,即360亿次记录。然而,就在几个小时前发生了冲突,当时数据库中只有530万条现有记录。我是非常不走运,还是我的ID生成函数在随机性方面有缺陷?我知道mt_rand()不是真正随机的,但它足够随机了,不是吗


我本来会编写一个循环来检查生成的ID是否唯一,如果不唯一,则生成一个新ID,但我认为发生冲突的可能性太小了,这样一个循环的性能成本是不值得的。现在,我将在代码中包含这样一个循环,但如果ID生成函数确实存在缺陷,我仍有兴趣完善它。

如果您想要保证唯一的16字节ID,那么我将使用加密。AES使用16字节(128位)块,只要输入是唯一的,输出也保证是唯一的

在ECB模式下设置AES(更简单、更快),并加密数字0、1、2、3、4。。。您的输入是唯一的,因此输出也是唯一的


加密站点会告诉您ECB模式存在安全问题,但这些问题仅适用于输入不唯一的情况。对于唯一的“随机”数字生成,根据您的要求,这些问题不适用,因为您的输入都是唯一的。

PHP中的
mt_rand()
的实现相当流畅,因此不同版本的实现可能有所不同。但是,以下是PHP版本5中使用的代码的一些摘录:

:
/*MT兰特*/
#定义PHP_MT_RAND_MAX((长)(0x7FFFFFFF))/*(1输出
完成&
正如所料,第一次碰撞发生在大约216次迭代之后(远未接近2616次):


$sort大多数数据库管理系统都具有生成UUID的功能,这些UUID保证对特定数据库实例是唯一的。为什么不使用这些UUID?由于空间限制,我将UUID替换为这种基本36 ID。我需要将尽可能多的信息打包到一点数据库空间中,同时仍然使用生成足够长且复杂的ID以避免冲突的算法。uuid位于基数16中,包含破折号和一些非随机字符,因此它们不像我希望的那样节省空间。您所描述的只是uuid的字符串表示。uuid实际上是一个二进制结构,大小正好为16字节。大多数数据库管理系统特别支持uuid列类型,然后存储16字节二进制表示,而不是字符串表示。您使用什么数据库管理系统?我只是读了一点mt_rand基,我很难相信这是基于随机数生成器的。您是如何生成种子的?可能有一个llision?例如,将时间作为种子并并行执行程序几次?MySQL不直接支持uuid列类型,但您可以将uuid存储在varbyte(16)中列,并使用内置函数uuid_to_bin/bin_to_uuid在字符串和二进制表示之间进行转换。这很有趣。random_int()怎么样?在随机性方面是否比mt_rand()好?@Jeff好得多。根据,
random_int()
默认从
/dev/uradom
获取随机字节,因此20位基数-36数字之间发生冲突的可能性非常小。但当然不是零。如果冲突会产生严重后果,请改用AES加密。仅供参考,在单调递增的值上使用ECB模式称为计数器(CTR)模式,并且您的加密库可能对此有直接支持。请注意,建议将前64位设置为随机值(nonce)。