我正在寻找一种方法,特别是在 PHP 中,保证我始终获得唯一的密钥。

我做了以下事情:

strtolower(substr(crypt(time()), 0, 7));

但我发现,偶尔我会得到一个重复的密钥(很少,但经常发生)。

我也想过这样做:

strtolower(substr(crypt(uniqid(rand(), true)), 0, 7));

但根据 PHP 网站,uniqid() 可以,如果在同一微秒内调用 uniqid() 两次,它可以生成相同的密钥。我认为添加 rand() 很少会,但仍然有可能。

在上面提到的行之后,我还删除了 L 和 O 等字符,这样用户就不会感到困惑了。这可能是重复的部分原因,但仍然是必要的。

我想到的一个选择是创建一个网站来生成密钥,将其存储在数据库中,确保其完全唯一。

还有其他想法吗?是否有任何网站已经执行此操作并具有某种 API 或仅返回密钥。我发现 http://userident.com 但我不确定密钥是否完全唯一。

这需要在后台运行,无需任何用户输入。

有帮助吗?

解决方案

只有 3 种方法可以生成唯一值,即密码、用户 ID 等:

  1. 使用有效的 GUID 生成器 - 它们很长且无法缩小。如果你只使用部分 你失败了.
  2. 至少部分数字是根据单个序列按顺序生成的。您可以添加绒毛或编码,使其看起来不那么连续。优点是起步时间短,缺点是需要单一来源。解决单一源限制的方法是使用编号的源,因此您可以包含 [source #] + [seq #],然后每个源都可以生成自己的序列。
  3. 通过其他方式生成它们,然后根据先前生成的值的单一历史记录检查它们。

不保证任何其他方法。请记住,从根本上讲,您正在生成一个二进制数(它是一台计算机),但随后您可以将其编码为十六进制、十进制、Base64 或单词列表。选择适合您用途的编码。通常,对于用户输入的数据,您需要 Base32 的某种变体(您暗示过)。

关于 GUID 的注意事项: :它们的独特性来自于它们的长度和生成它们的方法。 任何低于 128 位的数据都是不安全的。 除了随机数生成之外,GUID 中还有一些特征使其更加独特。请记住,它们只是实际上独一无二,而不是完全独一无二。虽然实际上不可能有重复的,但这是可能的。

关于 GUIDS 的更新说明: :自从写这篇文章以来,我了解到许多 GUID 生成器使用加密安全的随机数生成器(很难或不可能预测生成的下一个数字,并且不太可能重复)。实际上有5种不同 UUID算法. 。算法 4 是 Microsoft 目前用于 Windows GUID 生成 API 的算法。A GUID 是微软对UUID标准的实现。

更新: :如果您需要 7 到 16 个字符,则需要使用方法 2 或 3。

底线: :坦率地说,没有什么东西是完全独特的。即使您使用顺序生成器,您最终也会耗尽宇宙中所有原子的存储空间,从而循环并重复。你唯一的希望是宇宙在到达那个点之前热寂。

即使最好的随机数生成器也有可能重复等于您生成的随机数的总大小。以四分之一为例。它是一个完全随机的位生成器,重复的几率是二分之一。

所以这一切都取决于你的独特性门槛。通过使用序列然后对其进行 Base32 编码,您可以使 1,099,511,627,776 个数字的 8 位数字具有 100% 的唯一性。任何其他不涉及检查过去数字列表的方法仅具有等于 n/1,099,511,627,776(其中 n=生成的先前数字的数量)的不唯一的几率。

其他提示

任何算法都会导致重复.

因此,我是否可以建议您使用现有的算法*并简单地检查重复项?

*轻微补充:如果 uniqid() 基于时间可以是非唯一的,还包括在每次调用后递增的全局计数器。这样,即使在同一微秒内,某些东西也会有所不同。

如果不写代码,我的逻辑是:

从您喜欢的任何可接受的字符生成随机字符串。
然后将一半的日期标记(部分秒和全部)添加到前面,另一半添加到末尾(或者如果您愿意,可以添加到中间的某个位置)。

保持快乐!
H

如果您使用原来的方法,但在密码前面添加用户名或电子邮件地址,则如果每个用户只能有 1 个密码,则它将始终是唯一的。

您可能对这篇讨论同一问题的文章感兴趣: GUID 是全局唯一的,但 GUID 的子字符串不是.

该算法的目标是使用时间和位置的组合(相对论极客的“时空坐标”)作为唯一性密钥。然而,计时并不完美,因此有可能,例如,同一台机器快速连续生成两个 GUID,它们在时间上彼此非常接近,以至于时间戳相同。这就是唯一符发挥作用的地方。

我通常这样做:

$this->password = '';

for($i=0; $i<10; $i++)
{
    if($i%2 == 0)
        $this->password .= chr(rand(65,90));
    if($i%3 == 0)
        $this->password .= chr(rand(97,122));
    if($i%4 == 0)
        $this->password .= chr(rand(48,57));
}

我认为存在一些理论上的漏洞,但我从未遇到过重复问题。我通常将它用于临时密码(例如密码重置后),并且它的效果足够好。

正如 Frank Kreuger 所说,使用 GUID 生成器。

喜欢 这个

我仍然不明白为什么密码必须是唯一的?如果您的 2 个用户使用相同的密码,会有什么坏处?

这是假设我们正在讨论与用户 ID 相关的密码,而不仅仅是唯一标识符。如果 那是 您正在寻找什么,为什么不使用 GUID?

您可能对 Steve Gibson 密码生成器的超安全实现感兴趣(没有来源,但他有关于其工作原理的详细描述): https://www.grc.com/passwords.htm.

该网站创建了巨大的 64 个字符的密码,但由于它们是完全随机的,因此您可以轻松地使用前 8 个(或任意多个)字符来获得安全性较低但“尽可能随机”的密码。

编辑:从您后来的回答中我发现您需要更像 GUID 而不是密码的东西,所以这可能不是您想要的......

我确实相信你的问题的一部分是你正在尝试为两个不同的用途提供一个单一的功能......密码和 transaction_id

这确实是两个不同的问题领域,尝试一起解决它们确实不是最好的选择。

我最近想要一个快速而简单的随机唯一密钥,所以我执行了以下操作:

$ukey = dechex(time()) . crypt( time() . md5(microtime() + mt_rand(0, 100000)) ); 

所以,基本上,我得到了以秒为单位的unix时间,并添加了一个由时间+随机数生成的随机md5字符串。它不是最好的,但对于低频请求来说已经相当不错了。它快速且有效。

我做了一个测试,生成数千个键,然后查找重复项,每秒大约 800 个键,没有重复,所以还不错。我想这完全取决于 mt_rand()

我将它用于调查跟踪器,我们得到的调查提交率为每分钟约 1000 个调查...所以现在(交叉手指)没有重复项。当然,比率不是恒定的(我们在一天中的某些时间收到提交),因此这不是失败证明,也不是最佳解决方案......提示是使用增量值作为键的一部分(在我的例子中,我使用了 time(),但可能更好)。

忽略与创建唯一值没有太大关系的加密部分,我通常使用这个:

function GetUniqueValue()
{
   static $counter = 0; //initalized only 1st time function is called
   return strtr(microtime(), array('.' => '', ' ' => '')) . $counter++;
}

当在同一进程中调用时,$counter 会增加,因此值在同一进程中始终是唯一的。

当在不同的进程中调用时,您必须非常不幸地获得具有相同值的 2 个 microtime() 调用,认为在同一脚本中调用时 microtime() 调用通常也具有不同的值。

我通常会做一个随机子字符串(随机化 8 到 32 之间的字符数,或者为了用户方便而更少)或我获得的某个值的 MD5,或者时间,或者某种组合。为了获得更多的随机性,我对来值(比如姓氏)进行 MD5,将其与时间连接起来,再次 MD5,然后获取随机子字符串。是的,就是你 可以 获得相同的密码,但这根本不可能。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top