【问题标题】:Help me understand pack(), openssl_random_pseudo_bytes() and mt_rand() for salting passwords帮助我理解 pack()、openssl_random_pseudo_bytes() 和 mt_rand() 用于加盐密码
【发布时间】:2011-06-24 13:00:24
【问题描述】:

我正在构建一个拥有用户群的应用程序,并且我正处于保护登录的阶段。我对编程(和 PHP)还很陌生,但到目前为止,我的努力指向使用 Crypt() 和 Blowfish 散列盐。

在我继续之前,让我说明一下我目前对 phpass 不感兴趣

crypt() 文档中,一位用户最近发布了以下内容:

<?php 
   $salt = substr(str_replace('+', '.', base64_encode(pack('N4', mt_rand(), mt_rand(), mt_rand(), mt_rand()))), 0, 22); 
?>

它旨在用于系统 其中 mt_getrandmax() == 2147483647。

创建的盐将是 128 位 长度,填充到 132 位,然后 以 22 个 base64 字符表示。 (CRYPT_BLOWFISH 仅使用 128 位 盐,即使有 132 22 个 base64 字符中的位。如果你 检查 CRYPT_BLOWFISH 输入和 输出,你可以看到它忽略了 输入的最后四位,并设置 它们在输出时为零。)

请注意, 由返回的四个 32 位双字 mt_rand() 将始终为零(因为 mt_getrandmax == 2^31),所以只有 124 个 128 位将是伪随机的。一世 发现我可以接受 应用。

我测试了我的服务器,并且确实 mt_getrandmax() 返回 2147483647。我尝试查看文档以了解上述代码的实际作用——pack() 代码 N4 用于 32 位字符串(大端字节序??) 重复 4 次...我认为这就是为什么有 4 个 mt_rand() 参数。

我不明白的是他为什么用. 替换+ 以及22 个base64 字符的用途(并不是我完全理解base64 是什么。)

建议我查看openssl_random_pseudo_bytes() 来生成随机盐,因为我之前查看的方法仅限于1234567890abcdefghijklmnopqrstuvwxyz

据说5.3.4 之前存在一个错误,导致openssl_random_pseudo_bytes() 运行缓慢,偶尔会导致超时错误。我不确定我是否应该尝试将openssl_random_pseudo_bytes()Crypt() 一起使用,或者类似于上述使用mt_rand()pack() 的方法。

我试图更多地了解所有这些元素是如何工作的,以及它们在概念上的作用——而不是仅仅使用一个而不理解它来实现我的目标;我正在努力学习:P

有人可以帮助我了解这里工作的不同元素,或者至少将我引导到一个我可以阅读它的知识库吗?我认为最难理解的部分是理解不同的格式/术语(base64、ascii、hexdec、位、字节等),但最终,如何获得相当安全的盐以用于我的密码。

【问题讨论】:

    标签: php algorithm random salt


    【解决方案1】:

    首先让我说,从世代的角度来看,盐并没有什么特别之处。这只是另一个随机字符串。它的使用方式很特别,但不是生成的。

    您的具体问题

    1. 他为什么要把+换成.

      我不知道。可能是因为 + 字符可能与 url 中的空格混淆。但是盐永远不应该在网址中,所以很可能不是。

    2. base64/hexdec 是做什么的:

      Base64 将原始字节流(每个字节的值从 0 到 255)转换为 base 64 表示。上面有很多资源,所以不值得深入。阅读the wikipedia article了解更多信息。

      hexdec 将十六进制数 (a-f0-9) 转换为十进制数。它将基数 16 转换为基数 10(只是表示数字的另一种方式)。

    3. 什么是位和字节:

      比特是一个单一的信息单位。它有 2 个状态,0 或 1。一个字节是一系列 8 位。所以一个字节可以有 256 个独特的组合。阅读Wikipedia...

    4. 什么是ascii

      这是一个字符集。它表示单个 8 位字节中的单个可打印字符。同样,我建议阅读Wikipedia

    一般盐

    一个好的盐生成函数的目标是大熵。这意味着可能的输出数量尽可能大。所以任何方法都应该产生大量的结果。

    现在,您需要为盐定义可接受的字符(因为您需要存储盐以验证哈希)。最好的盐是全字节数字,而不仅仅是可显示的字符。现在,您将无法在有意义的庄园中展示它,但您不需要展示它。另外,对于存储,您始终可以使用 base64_encode 它。

    接下来,您需要选择盐的大小。盐越大越好。 32 个字符的盐是可以接受的,但 128 个字符的盐更好。盐的大小和每个字符的选项数量将决定存在的可能性数量。一些常见的组合:

    Hex, 32 characters: 2e38 possibilities
    Hex, 128 characters: 1e154 possibilities
    Full Byte, 32 characters: 1e77 possibilities
    Full Byte, 128 characters: 1e308 possibilities
    

    现在,您需要生成盐。关键是根据需要进行尽可能多的随机调用来填充熵。您可以通过以下几种方式做到这一点:

    • 系统相关(仅适用于 *nix 但熵最好):

      $f = fopen('/dev/urandom', 'r');
      $seed = fgets($f, $characters); // note that this will always return full bytes
      fclose($f);
      
    • 依赖库(很好,但需要安装 OpenSSL)

      $seed = openssl_random_pseudo_bytes($characters);
      
    • 后备

      $seed = '';
      for ($i = 0; $i < $characters; $i++) {
          $seed .= chr(mt_rand(0, 255));
      }
      

    现在,您需要将其转换为所需的输出格式。

    • 十六进制(a-f0-9):

      $out = '';
      for ($i = 0, $len = strlen($seed); $i < $len; $i++) {
          $num = ord($seed);
          $out .= dechex(floor($num / 16)) . dechex($num % 16);
      }
      
    • Base36 (a-z0-9):

      $out = '';
      for ($i = 0, $len = strlen($seed); $i < $len; $i++) {
          $num = ord($seed);
          $out .= base_convert($num, 10, 36);
      }
      
    • Base64 (a-zA-Z0-9+=):

      $out = base64_encode($seed);
      
    • 完整字节:

      没有必要,因为它已经是这种格式了。

    【讨论】:

    • @ircmaxell - 感谢您抽出宝贵时间回复 - 希望您不介意我还有几个问题:所以有 256 个唯一字节 (0-255),每个字节由 8 个组成0/1 组合。如果我使用chr() 来显示一个ascii 字符——这对我来说意味着有256 个ascii 代码,但它说有128 个带有33 个控制代码......剩下的呢?还是多字节字符?
    • @ircmaxell - 32 个字符对 128 个字符?字符是位的同义词吗? 32 位与 32 字符?如果他们说 128 位加密,那是否意味着它有 128 个字符长?另外,您可能的组合示例中的e 是什么?逗号?
    • ASCII 本身只使用代码点 0 到 127(它是一个 7 位字符集)。第 8 位在传统 ASCII 中未使用。但是,ISO-8859-1 (Latin-1) 使用第 8 位添加额外的 128 个代码点。因此,当我们使用chr() 时,我们将一个从 0 到 255 的数字转换为相关的二进制字节作为字符串(因此 65 变成了代码点 65,即字母 A)。它确实比这更复杂,因为代码点在不使用字符集的情况下具有值,但在 PHP 中我们不需要进行区分。想想chr() 将一个数字转换为相关的代码点。
    • @Julian: e 是科学记数法(1e21 x 10^21001e3081 x 10^308 这是 1 后跟 308 0 的。字符表示符号,不等同于位,128位不一定是128个字符,128位以Hex(a-f0-9)显示为32个字符,128位以全字节字符(0-255个码位)显示为16字符。当他们说位数时,它是唯一可能组合的数量。所以 128bit 有 2^1283.4e38 组合。
    • 啊,科学概念又回到了我的脑海中——我已经很久没有处理它了。我想要一个关于计算机科学的 lynda.com 培训视频,哈哈……谢谢你的帮助——我可以继续提问,但这不是 ircmaxelloverflow ;)
    猜你喜欢
    • 2013-09-22
    • 1970-01-01
    • 2015-04-07
    • 2015-02-10
    • 2016-04-09
    • 1970-01-01
    • 2014-11-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多