【问题标题】:Will using a substring of an MD5 hash like this be unique enough?使用这样的 MD5 哈希的子字符串是否足够独特?
【发布时间】:2010-02-14 04:03:35
【问题描述】:

我正在尝试为我网站上的文章创建一个 12 个字符的 ID,类似于 youtube 如何处理他们的视频 ID (http://www.youtube.com/watch?v=53iddd5IcSU)。现在我正在生成一个 MD5 哈希,然后像这样抓取它的 12 个字符:

$ArticleId = substr(MD5("Article".$currentID),10,12)

其中 $currentID 是数据库中的数字 ID(例如 144)

我有点偏执,我会遇到重复的 $ArticleId,但实际上发生这种情况的可能性有多大?而且,由于我的数据库中的列是唯一的,我怎样才能处理这种罕见的情况而不会抛出一个丑陋的错误?

附:我制作了一个小脚本来检查前 5000 个 $ArticleId 中的重复项,但没有。

编辑:我不喜欢 base64_encode 哈希的样子,所以我这样做了:

function retryAID($currentID)
{
    $AID = substr(MD5("Article".$currentID*2),10,12);

    $setAID = "UPDATE `table` SET  `artID` =  '$AID' WHERE `id` = $currentID ";
    mysql_query($setLID) or retryAID($currentID);
}


$AID = substr(MD5("Article".$currentID),10,12);

$setAID = "UPDATE `table` SET  `artID` =  '$AID' WHERE `id` = $currentID ";
mysql_query($setAID) or retryAID($currentID);

由于 AID 列是唯一的,mysql_query 会抛出错误,retryAID 函数会找到唯一的 id...

【问题讨论】:

标签: php md5 uniqueidentifier


【解决方案1】:

使用顺序 ID 有什么问题?数据库将为您处理。

除此之外,12 个字符仍然是 96 位。 296 = 79228162514264337593543950336 个可能的哈希值。尽管已知 MD5 存在碰撞漏洞,但发生碰撞的可能性与实际看到碰撞的概率之间存在天壤之别。

更新:

根据您使用的 PHP md5 函数的返回值,我上面的数字不太正确。

将哈希值作为 32 个字符的十六进制数字返回。

由于您要从 32 个字符的十六进制数字中提取 12 个字符(而不是 128 位哈希的 12 个字节),因此最终可能得到的实际哈希数是 1612 = 281474976710656。还是不少。

【讨论】:

  • 最简单的答案几乎总是最好的!
  • md5 仅以 16 为基数,因此实际上“仅”有 16^12 个可能的值(281,474,976,710,656)。冲突的可能性取决于 md5 散列的 12 个连续值的可变性。 (编辑:n/m,我想你已经指出了这一点!)
  • MD5 碰撞漏洞与此应用无关。
【解决方案2】:
<?php
  function get_id()
  {
    $max = 1679615; // pow(36, 4) - 1;
    $id = '';

    for ($i = 0; $i < 3; ++$i)
    {
      $r = mt_rand(0, $max);
      $id .= str_pad(base_convert($r, 10, 36), 4, "0", STR_PAD_LEFT);
    }
    return $id;
  }
?>

返回一个以 36 为基数的 12 个字符的数字,提供 4,738,381,338,321,616,896 种可能性。 (碰撞概率取决于随机数发生器的分布。)

为确保没有冲突,您需要循环:

<?php
do {
  $id = get_id();
} while ( !update_id($id) );
?>

【讨论】:

  • 你能解释一下你使用 str_pad 函数的意图吗?它似乎没有做任何事情。我猜是为了确保 base_convert 结果肯定是 4 个字符?或者可能将类型转换为字符串?
  • 填充是为了确保三个部分中的每一个都恰好是四个字符长。例如,base_convert(0, 10, 36) 将产生 0,但使用填充它将是 0000
  • 您预计在 36**6 = 2,176,782,336 次 get_id() 调用后会发生冲突。这是一个很大的数字,但我仍然会使用您的确保无冲突循环。
【解决方案3】:

不是不是很独特。

如果您需要更短,为什么不对其进行 base64 编码?

【讨论】:

  • 我认为他想混淆它。
【解决方案4】:

【讨论】:

    猜你喜欢
    • 2011-01-27
    • 1970-01-01
    • 1970-01-01
    • 2010-10-26
    • 2019-08-08
    • 2012-10-10
    • 1970-01-01
    • 1970-01-01
    • 2011-04-18
    相关资源
    最近更新 更多