PHP函数生成一个无限唯一的标识符

PHP函数生成一个无限唯一的标识符,php,security,zend-framework,zend-framework2,zend-framework3,Php,Security,Zend Framework,Zend Framework2,Zend Framework3,我正在Zend 3中创建一个新项目,它要求我有一个唯一的ID或散列,以后可以在多个地方使用 我在Google上查看了很多例子,但找不到一个能够满足我要求的函数,因为它需要始终保持99%的唯一性,并且它需要能够始终生成数以亿计的唯一“哈希” 以下功能引起了我的注意: function uniqidReal($lenght = 13) { // uniqid gives 13 chars, but you could adjust it to your needs.

我正在Zend 3中创建一个新项目,它要求我有一个唯一的ID或散列,以后可以在多个地方使用

我在Google上查看了很多例子,但找不到一个能够满足我要求的函数,因为它需要始终保持99%的唯一性,并且它需要能够始终生成数以亿计的唯一“哈希”

以下功能引起了我的注意:

  function uniqidReal($lenght = 13) {
        // uniqid gives 13 chars, but you could adjust it to your needs.
        if (function_exists("random_bytes")) {
            $bytes = random_bytes(ceil($lenght / 2));
        } elseif (function_exists("openssl_random_pseudo_bytes")) {
            $bytes = openssl_random_pseudo_bytes(ceil($lenght / 2));
        } else {
            throw new Exception("no cryptographically secure random function available");
        }
        return substr(bin2hex($bytes), 0, $lenght);
    }
一个简单的测试:

echo "<pre>";

for($i = 0; $i < 100; $i++)
{
    echo $this->uniqidReal(25) .PHP_EOL ;   
}
但我无法确认这是否能保证我的生产环境有99%的成功率


如果有人能给我建议,或给我举个例子,我将不胜感激

这应该是一个评论,但有点长

你对“独特”和“一直”的使用有些混淆。令牌要么是唯一的,要么不是唯一的。单独使用随机数生成器创建令牌不足以保证唯一性-随机数生成器的关键在于您不知道要生成的下一个值是什么-这意味着您也不知道下一个数字将与前一个数字不同。OTOH,使用random_bytes()或openssl_random_pseudo_bytes()生成一个“99%始终唯一”的令牌似乎是一个巨大的滥杀

为了弄清楚这可能有多独特,我们需要知道在任何时候,有多少代币将在群体中被考虑(或者能够从预期的创建速率和TTL计算出来)

您使用的是大数字,这意味着您有很好的理由不使用最简单和最明显的唯一标识符,即递增整数。因此,对猜测现有标识符的抵制显然是实现的关键——但您也没有告诉我们这一点

将文章标题粘贴到Google中会显示文章的顶部结果(紧随其后的()函数),但出于某种原因,您可能没有找到uniqid(),或者出于某种原因拒绝了它

你文章的标题也是一个矛盾修饰法——为了定义一组无限长的标识符,标识符的长度必须是无限长的

它需要能够生成数以亿计的“散列”

……您希望所有这些都在Zend框架内运行吗?-哈哈

但我无法确认这是否能保证我的生产环境有99%的成功率


为什么不呢?这里有足够的信息来确认按位熵是均匀分布的,并且应该知道生产环境的计划容量。剩下的是基本的算术。

函数
随机字节
生成加密安全的随机字节

对于
openssl_random_pseudo_bytes
添加
crypto_strong
parametre,以确保所使用的算法加密性强


由于您的要求仅为99%,唯一加密安全的随机字节将满足您的要求。

我们约为8x10⁹ 人。想象一下,在一年中,我们每秒钟访问一次您的站点,需要一个唯一的标识符。您需要大约252288×10个标识符。如果您认为您的站点将投入生产大约1000年,并且人口将增加1000倍,那么您需要大约10²⁹ 标识符;所以一个32字节的自动增量字符串就足够了。添加伪随机32字节字符串作为后缀,以获取安全的64字节标识符。再加上一点,您可以散列标识符来创建令牌

然后很容易编写一个函数来获取它们

2017年4月13日编辑 小样本: 首先需要一个伪随机强密钥生成器。我将发布我当前使用的函数:

<?php
function pseudo_random_key($byte_count = 32) {
  return base64_encode(pseudoRandomBytes($byte_count));
}

如果你想保证你的ID是唯一的,你必须保护你的ID。但是如果你把微时间也算进去呢
microtime().$yourKey
这样就足够了吗?@user2659982-不幸的是,这样不行。。。看看CloudFlare->Zone ID,例如,他们有很多网站,每个网站都有不同的唯一区域ID。他们将它们保存在数据库中,然后您可以在生成唯一ID时查找它们。我计划将其存储在数据库中,以便以后使用它。UUID4可能对您有帮助。在packagist.org中有很多库支持该算法。我拒绝了uniqid(),因为它不能保证它的功能,请阅读并亲自查看php.net-而且我可能还没有很好地解释自己,“它需要能够生成成百上千万个”散列…如果您无法衡量您引用的随机数方法的有效性,您如何衡量uniqid()的有效性?正如PHP.net所说:“此函数不保证返回值的唯一性。由于大多数系统通过NTP或类似方式调整系统时钟,因此系统时间不断变化。因此,此函数可能不会返回进程/线程的唯一ID。使用更多的熵来增加唯一性的可能性。“您已经说过,您不需要该值始终是唯一的。您衡量质量的标准是互联网上某个随机的人这么说的吗?谢谢,密码应该是这样的?请参阅:如果
crypto\u strong
被传递到函数中,这将包含一个布尔值,确定是否使用了该算法。”“Cryptographics strong”,如果正确,则为TRUE,否则为FALSE。谢谢,我将使用此函数。谢谢,它确实有帮助,但举个例子会更好:)
<?php
function pseudoRandomBytes($count = 32){
  static $random_state, $bytes, $has_openssl, $has_hash;
  $missing_bytes = $count - strlen($bytes);
  if ($missing_bytes > 0) {
  // If you are using a Php version before 5.3.4 avoid using
  // openssl_random_pseudo_bytes()
    if (!isset($has_openssl)) {
      $has_openssl = version_compare(PHP_VERSION, '5.3.4', '>=') 
                   && function_exists('openssl_random_pseudo_bytes');
    }
    // to get entropy
    if ($has_openssl) {
      $bytes .= openssl_random_pseudo_bytes($missing_bytes);
    } elseif ($fh = @fopen('/dev/urandom', 'rb')) {
      // avoiding openssl_random_pseudo_bytes()
      // you find entropy at /dev/urandom usually available in most
      // *nix systems
      $bytes .= fread($fh, max(4096, $missing_bytes));
      fclose($fh);
    }
    // If it fails you must create enough entropy
    if (strlen($bytes) < $count) {
      // Initialize on the first call. The contents of $_SERVER 
      // includes a mix of user-specific and system information 
      // that varies a little with each page.
      if (!isset($random_state)) {
        $random_state = print_r($_SERVER, TRUE);
        if (function_exists('getmypid')) {
          // Further initialize with the somewhat random PHP process ID.
          $random_state .= getmypid();
        }
        // hash() is only available in PHP 5.1.2+ or via PECL.
        $has_hash = function_exists('hash') 
                  && in_array('sha256', hash_algos());
        $bytes = '';
      }
      if ($has_hash) {
        do {
          $random_state = hash('sha256', microtime() . mt_rand() . 
          $random_state);
          $bytes .= hash('sha256', mt_rand() . $random_state, TRUE);
        } while (strlen($bytes) < $count);
      } else {
        do {
          $random_state = md5(microtime() . mt_rand() . $random_state);
          $bytes .= pack("H*", md5(mt_rand() . $random_state));
        } while (strlen($bytes) < $count);
      }
    }
  }
  $output = substr($bytes, 0, $count);
  $bytes = substr($bytes, $count);
  return $output;
}
<?php
function pseudo_random_key($byte_count = 32) {
  return base64_encode(pseudoRandomBytes($byte_count));
}
<?php
function uniqueChunkMathKeysPrefix(){
  // a call to read your db for prefix
  // I suppose you have an environment string-keyed table
  // and a couple of dbfunction to read and write data to it
  $last18bytesPrefix = dbReadEnvVariable('unique_prefix');
  // Also you store your current index wich returns to 0 once you get
  // a 99999999999999 value
  $lastuniqueindex = dbReadEnvVariable('last_unique_keys_index');
  if ($lastuniqueindex < 99999999999999){
    $currentuniqueindex = $lastuniqueindex + 1;
    $curret18bytesPrefix = $last18bytesPrefix;
  }else{
    $currentuniqueindex = 0;
    $curret18bytesPrefix = dbReadEnvVariable('next_unique_prefix');
    // flag your db variables to notify cron to create a new next prefix
    dbStoreEnvVariable('next_unique_prefix', 0);
    dbStoreEnvVariable('unique_prefix', $curret18bytesPrefix);
    // you have the time needed to have site visits and create new 
    // 99999999999999 keys as a while to run your cron to adjust your 
    // next prefix
  }
  // store your current index
  dbStoreEnvVariable('last_unique_keys_index', $currentuniqueindex);
  // Finally you create the unique index prefix part
  $uniqueindexchunk = substr('00000000000000'.$currentuniqueindex, -14);
  // return the output
  return $curret18bytesPrefix.$uniqueindexchunk;
}
<?php
function createUniquePseudoRandomKey(){
  $newkey = uniqueChunkMathKeysPrefix() . pseudo_random_key(32);
  // to beautify the output make a dummie call
  // masking the 0s ties
  return md5($newkey);
}