【问题标题】:Shorter GUIDs than hashing a user id?比散列用户 ID 更短的 GUID?
【发布时间】:2011-01-11 03:41:20
【问题描述】:

我想知道 Instapaper(保存文本的书签)如何为其书签生成 URL。

我的脚本 src 类似于 www.instapaper.com/j/AnJHrfoDTRia

这些 URL 的质量是它们永远不会发生冲突,并且不能被猜测(因此其他人无法保存到您的帐户)。

我知道一个简单的方法可能是对他们的电子邮件地址进行 MD5 处理(假设在注册时已检查其唯一性),但最终我会得到一个超长字符串。这不是一个大问题,但我想知道有哪些技术可以让较短的 GUID 不会经常发生冲突(这显然是一种权衡,但在我看来,上面的 12 个字符非常短)

【问题讨论】:

  • 如果您的数据库中已经有(自动递增)整数 ID 的条目,只需使用 (new Id())->encode($id) 来获取它们所拥有的内容。这是完全无冲突的,但模糊且简短。

标签: php guid user-management


【解决方案1】:

您可以通过将 MD5 哈希视为以 16 为底的数字(使用字符 (0-9a-f) 并将其转换为例如以 36 为底的数字)来获得更短的字符串。

<?php
function gmp_convert($num, $base_a, $base_b) {
    return gmp_strval (gmp_init($num, $base_a), $base_b );
}

$hash = md5("hello");
$hash2 = gmp_convert($hash,16,36);
echo "$hash <br>"; //5d41402abc4b2a76b9719d911017c592 
echo $hash2; //5ir3t0ozoelrnauhrwyu1xfgy

您提到的链接似乎使用了所有字母(大写和小写)。

these Q&As提取的信息

【讨论】:

    【解决方案2】:
    <?php
    
    $length = 12;
    
    $chars = array_merge(range(0, 9), range('a', 'z'), range('A', 'Z'));
    
    $hash = '';
    
    for ($i = 0; $i < $length; $i++) {
        $hash .= $chars[array_rand($chars)];
    }
    
    var_dump($hash);
    

    这将为我们提供 3226266762397899821056 独特组合,而 md5 为 281474976710656(1100 万倍)。

    只需 4 个字符 (!!!) 就会有 14776336 个独特的组合,这对您来说已经足够了。

    【讨论】:

      【解决方案3】:

      Base64 对一组加密性强的随机数进行编码。

      <?php
      // get 72 pseudorandom bits in a base64 string of 12 characters
      
      $pr_bits = '';
      
      // Unix/Linux platform?
      $fp = @fopen('/dev/urandom','rb');
      if ($fp !== FALSE) {
          $pr_bits .= @fread($fp,9);
          @fclose($fp);
      }
      
      // MS-Windows platform?
      if (@class_exists('COM')) {
          // http://msdn.microsoft.com/en-us/library/aa388176(VS.85).aspx
          try {
              $CAPI_Util = new COM('CAPICOM.Utilities.1');
              $pr_bits .= $CAPI_Util->GetRandom(9,0);
      
              // if we ask for binary data PHP munges it, so we
              // request base64 return value.  We squeeze out the
              // redundancy and useless ==CRLF by hashing...
              if ($pr_bits) { $pr_bits = substr(md5($pr_bits,TRUE), 0, 9); }
          } catch (Exception $ex) {
              // echo 'Exception: ' . $ex->getMessage();
          }
      }
      
      $uid = base64_encode($pr_bits);
      ?>
      

      这将在 12 个字符中为您提供 72 位最纯粹的哥伦比亚人。这组大约包含 10^21 个数字。这意味着在 100 万用户之后发生碰撞的几率大约为十亿分之一。

      这是对这个 stackoverflow 答案的一个非常小的修改,用于产生加密效果:Secure random number generation in PHP

      【讨论】:

        【解决方案4】:

        MD5 用户名。获取生成的 MD5 哈希的前 X 个字符。检查数据库中是否已经存在具有该值的 url 令牌。如果是这样,请取前 X+1 个字符并尝试(依此类推)。如果没有,那么您拥有该用户的令牌。将令牌存储在数据库中并从现在开始在那里查找 - 不要尝试每次都从用户名重新创建令牌。

        您可能从 X=7 开始并做得很好(绝大多数令牌生成不超过 1-2 次尝试)。

        此外,您可能希望在哈希计算中添加其他内容(例如,他们的或随机数),只是为了更难预测给定用户的令牌。

        【讨论】:

        • 有任何理由使用md5name,如果它有时会是+1 char(这意味着可预测)?为什么不只是md5(microtime(1))md5(uniqid())
        • @zerkms:所有完全可以接受的选项。你甚至可以只是md5(rand())
        • 为了增加容量,最好不要使用 md5,而是从 0-9a-zA-Z 随机生成字符。它将为我们提供 3226266762397899821056 个独特的组合,而 md5 为 281474976710656(大于 1100 万倍
        • 虽然一旦超过数百万/十亿大关,这并不总是重要的。
        • @Amber:但它允许使哈希 much 更短:1048576(显然不够)与 916132832 仅 5 个字符。即使是 4 个字符:65536 vs 14776336,第二个满足了我们的欲望,而第一个很糟糕。
        猜你喜欢
        • 1970-01-01
        • 2010-10-17
        • 1970-01-01
        • 2023-03-23
        • 1970-01-01
        • 2015-08-06
        • 2013-01-29
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多