【问题标题】:Generating a salt in PHP在 PHP 中生成盐
【发布时间】:2011-01-31 14:34:11
【问题描述】:

在不依赖于典型 PHP 安装中很少包含的库的情况下,在 PHP 中生成加密安全的 32 字节盐的最佳方法是什么?

经过一番谷歌搜索后,我发现mt_rand 被认为不够安全,但我还没有找到替代建议。一篇文章建议阅读/dev/random,但这不仅不适用于Windows;它也非常慢。

我希望在安全性和速度之间取得合理的平衡(即,生成 512 个字节不应需要 20 秒,就像 /dev/random 通常那样)

【问题讨论】:

  • /dev/urandom 可用于更快的生成,但仍无法在 Windows 系统上运行。

标签: php random cryptography salt


【解决方案1】:

我觉得microtime() 就够了。

奇怪的是,但我仍然对这个答案投了反对票。

虽然我得到的唯一解释是微时间是可预测的。
这对我来说听起来很奇怪,因为盐总是被认为是公开的 - 所以,预测根本没有用。

【讨论】:

  • 并非如此。不仅非常容易预测(现在是当前时间),我也无法从中得到 32 个字节。
  • @qster 您可以将其用作随机数生成器。并获得您想要的任意数量的字节。而且它不是当前时间,你用 time() 搞砸了。微秒确实变化很快。而且它只是盐,你不需要太多的盐安全。
  • @YourCommonSense 通过for循环运行echo microtime()(10个循环就可以了),看看你错了多少......
  • 奇怪的是,有些 cmets 甚至在一行中也可能包含矛盾:“这是当前时间 [stamp],但我无法从中获取 32 个字节”。我一直认为unix时间戳是一个32位的数字。
  • @YourCommonSense:位不是字节。
【解决方案2】:

注意mcrypt 在 PHP 7.1 中已被弃用。 Skip to the up-to-date answer.

您可能想查看mcrypt_create_iv() 的文档(和 cmets)。

【讨论】:

  • mcrypt 在 PHP 7.1 中已弃用。您不应该编写已弃用的代码。所以如果你在 2016 年读到这个,不要使用这个答案,更喜欢@Scott Arciszewski 的答案。
  • mcrypt_create_iv() 将被弃用,因此最好按照此处的建议使用 random_bytes() - us.php.net/manual/en/function.mcrypt-create-iv.php
【解决方案3】:

uniqid() 应该可以用于此目的。

【讨论】:

  • 我也使用 uniqid() 和一些快速可计算的种子值(如建议的 microtime)。尚未进行密码分析,但似乎符合要求。
【解决方案4】:

/dev/urandom 读取,或使用openssl_random_pseudo_bytes()

【讨论】:

    【解决方案5】:

    注意mcrypt 在 PHP 7.1 中已被弃用。 Skip to the up-to-date answer.

    您可以使用函数mycrypt_create_iv(),因为 PHP 版本 5.3 它还在 Windows 服务器上使用随机源(不仅在 Unix 上)。在使用之前,你应该检查常量MCRYPT_DEV_URANDOM是否被定义。

    mcrypt_create_iv($length, MCRYPT_DEV_URANDOM);
    

    与随机不同,如果没有足够的可用熵,urandom 不会阻塞服务器。由于密码 salt 应该是唯一的(不一定是随机的),所以 urandom 对我来说似乎是一个不错的选择。

    【讨论】:

    • 请注意,第二个参数是可选的,MCRYPT_DEV_URANDOM 是默认值 - 所以如果您不想更改它,也可以使用mcrypt_create_iv($length)。此外,此功能在mcrypt 扩展中实现,它可能安装在您的服务器上,也可能不安装。最后,该函数的结果是一个二进制字符串,这通常不是您需要的,因为您想保存盐以供以后参考。我通常在将结果用作盐之前base64_encode
    • @Guss - 这些都是好点。此外,需要检查盐的预期形式。例如,BCrypt 不接受二进制盐,但 base64 编码的盐也无效。对于密码哈希,最好使用内置函数password_hash(),它会自行生成一个安全的盐。
    • 不使用password_hash() 的一个原因是我没有实现密码。我需要一个盐来实现一个无状态的“api key”风格的授权协议。
    • 此外,password_hash() 并非在所有服务器上都可用。例如,我的服务器卡在 php 5.3 上。 password_hash() 直到 5.5 才可用。
    • @RoboticRenaissance - 这应该不是问题,5.3.7 及更高版本有一个compatibility pack。稍作调整,它甚至可以在 5.3 中使用。
    【解决方案6】:

    uniqueid 不太适合生成随机字符串,因为它也是基于 microtime 的。 CPU 周期通常比 microtime-tick 短得多,这可能导致循环内给定变量可能保持不变。 设置第二个参数“entropy”为true,

     uniqid('', true)
    

    会增加随机性。

    为了得到一个与大多数字符集很好兼容的随机字符串,可以将base64编码应用于mcrypt初始化向量函数mcrypt_create_iv

    $length = 16;
    base64_encode(mcrypt_create_iv(ceil(0.75*$length), MCRYPT_DEV_URANDOM))
    //> hlZuRJypdHFQPtI2oSFrgA==
    strlen(base64_encode(mcrypt_create_iv(ceil(0.75*$length), MCRYPT_DEV_URANDOM)))
    //> 16
    

    将字符字母减少到 2^6Bit 会增加大小,这已在上面进行了说明。

    【讨论】:

      【解决方案7】:

      这在 PHP 7 中更容易: 只需使用$salt = random_bytes($numberOfDesiredBytes); 生成盐。

      无论如何,你需要盐做什么?如果是密码,只需使用password_hash()password_verify()

      【讨论】:

      • 感谢您对password_hash 的关注。结果证明它比原来的解决方案更适合我。
      【解决方案8】:

      看起来这个问题有一个公认的答案,但我只是想在经过一些研究并阅读这个帖子后另外声明,如果你不把所有的鸡蛋都放在一个篮子里,你可能会增加一些安全性。

      我可能建议不要仅仅依靠 PHP 来创建你的盐和散列你的密码。如果您让数据库完成部分工作,您可以进一步混淆您的解决方案。

      有人建议只使用 password_hash() 和 password_verify()。虽然这些都是很好的方法,但我强烈建议坚持在这些方法之外加入盐的想法。

      要回答这个问题,盐可以是任何真正随机的并且强制执行为用户独有的东西。只要您遵守这两条规则,您就可以在技术上按照您的意愿生成它。

      几个不错的资源:

      https://www.codeproject.com/Articles/704865/Salted-Password-Hashing-Doing-it-Right https://auth0.com/blog/adding-salt-to-hashing-a-better-way-to-store-passwords/

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2013-12-21
        • 2017-12-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-04-28
        相关资源
        最近更新 更多