关于php:唯一密钥生成

关于php:唯一密钥生成

Unique key generation

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

我已经完成以下工作:

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

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

我还考虑过这样做:

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

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

在上述几行之后,我还将删除L和O之类的字符,因此对用户而言不那么混乱。这可能是重复的部分原因,但仍是必要的。

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

还有其他想法吗?是否有已经使用某种API或仅返回密钥的已经这样做的网站。我找到了http://userident.com,但不确定这些键是否完全唯一。

这需要在没有任何用户输入的情况下在后台运行。


生成唯一值的方法只有3种,它们是密码,用户ID等:

  • 使用有效的GUID生成器-这些生成器很长且不能缩小。如果仅使用部件,则将失败。
  • 至少一部分数字是从单个序列中顺序生成的。您可以添加绒毛或编码,以减少顺序感。优点是它们起步短-缺点是它们需要单一来源。单个来源限制的解决方法是为来源编号,因此您要包含[source#] + [seq#],然后每个来源都可以生成自己的序列。
  • 通过其他方式生成它们,然后对照先前生成的值的单个历史记录对其进行检查。
  • 不保证任何其他方法。请记住,从根本上讲,您正在生成一个二进制数(它是一台计算机),但是您可以将其编码为十六进制,十进制,Base64或单词列表。选择适合您用法的编码。通常,对于用户输入的数据,您需要Base32的一些变体(您曾暗示过)。

    关于GUIDS的注意事项:它们从其长度和生成它们的方法中获得了独特的优势。少于128位的任何内容都不安全。除了生成随机数外,GUID还具有一些特性,以使其更加独特。请记住,它们实际上几乎是唯一的,而不是完全唯一的。有可能,尽管实际上没有重复的可能。

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

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

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

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

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


    任何算法都将导致重复。

    因此,我是否建议您使用现有算法*并仅检查重复项?

    *轻微的增加:如果uniqid()根据时间可能是唯一的,则还包括一个全局计数器,您每次调用后都会增加该计数器。这样一来,即使在相同的微秒内,情况也会有所不同。


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

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

    在同一过程中调用$ counter时,其值将增加,因此在同一过程中值始终是唯一的。

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


    我最近想要一个快速简单的随机唯一键,所以我做了以下工作:

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

    因此,基本上,我得到以秒为单位的Unix时间,并添加一个由时间+随机数生成的随机md5字符串。这不是最好的,但是对于低频请求来说,这是非常好的。快速且有效。

    我做了一个测试,在该测试中,我将生成数千个密钥,然后查找重复项,并且每秒大约有800个密钥,没有重复项,所以还不错。我猜这完全取决于mt_rand()

    我将其用于调查跟踪器,在该跟踪器中,每分钟大约有1000项调查的提交速度...因此,现在(交叉手指)没有重复项。当然,费率不是恒定的(我们会在一天中的某些时间提交),所以这不是故障证明,也不是最佳的解决方案……提示是将增量值用作密钥的一部分(在我的情况下,我使用了time(),但可能会更好)。


    我确实相信您的问题的一部分是,您正在尝试为我们提供两种单独用途的单一功能...密码和transaction_id

    这些确实是两个不同的问题领域,并且最好不要一起解决它们。


    我仍然看不到为什么密码必须唯一?如果您的两个用户使用相同的密码,会有什么弊端?

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


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

    像这个


    您可能对史蒂夫·吉布森(Steve Gibson)的密码生成器的顶级安全实现感兴趣(无资料,但他对密码生成器的工作方式有详细说明),网址为https://www.grc.com/passwords.htm。

    该站点创建了巨大的64个字符的密码,但是由于它们是完全随机的,因此您可以轻松获取前8个(或多个)字符,以获得不太安全但"尽可能随机"的密码。

    编辑:从您以后的答案中,我看到您需要的像是GUID而不是密码,因此这可能不是您想要的...


    我通常这样做:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    $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));
    }

    我想有一些理论上的漏洞,但我从来没有遇到过重复问题。我通常将其用于临时密码(例如在重置密码后),并且足够有效。


    您可能对处理同一问题的本文感兴趣:GUID在全局上是唯一的,但GUID的子字符串却不是。

    The goal of this algorithm is to use the combination of time and location ("space-time coordinates" for the relativity geeks out there) as the uniqueness key. However, timekeeping is not perfect, so there's a possibility that, for example, two GUIDs are generated in rapid succession from the same machine, so close to each other in time that the timestamp would be the same. That's where the uniquifier comes in.


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


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

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

    保持快乐!
    H


    我通常会做一个随机的子字符串(为方便起见,随机分配8至32之间的几个字符)或获得的某个值的MD5,时间或某种组合。为了获得更大的随机性,我将值(例如姓氏)的MD5与时间连接起来,再次将MD5与时间连接,然后采用随机子字符串。是的,您可以获得相同的密码,但是不太可能。


    推荐阅读