在PHP中生成盐值

18

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

通过一些谷歌搜索,我发现mt_rand被认为不够安全,但是我没有找到替代建议。有一篇文章建议从/dev/random读取,但这不仅不适用于Windows;而且速度非常慢

我希望在安全性和速度之间取得合理的平衡(也就是说,它不应该像/dev/random通常那样花费20秒来生成512字节)。


2
/dev/urandom 可以用于更快的生成,但在 Windows 系统上仍无法使用。 - Amber
8个回答

25

1
谢谢关于password_hash的提醒。结果证明它对我比原来的解决方案更有效。 - user2286243

10

2
mcrypt在PHP 7.1中已被弃用。您不应编写已经被弃用的代码。因此,如果您在2016年阅读此内容,请勿使用此答案,而应选择@Scott Arciszewski的答案。 - JesusTheHun
mcrypt_create_iv() 将被弃用,建议使用此处建议的 random_bytes() - http://us.php.net/manual/en/function.mcrypt-create-iv.php - Kamal Joshi

9

注意:在PHP 7.1中,mcrypt已被弃用。 查看最新答案

您可以使用函数mycrypt_create_iv(),自PHP版本5.3以来它也使用Windows服务器上的随机源(不仅限于Unix)。在使用之前,应检查常量MCRYPT_DEV_URANDOM是否定义。

mcrypt_create_iv($length, MCRYPT_DEV_URANDOM);

与random不同,如果没有足够的熵可用,urandom不会阻塞服务器。由于密码盐应该是唯一的(不一定是随机的),我认为urandom是一个很好的选择。


请注意第二个参数是可选的,值MCRYPT_DEV_URANDOM是默认值 - 因此,如果您不想更改它,您可以使用mcrypt_create_iv($length)。此外,此功能是在mcrypt扩展中实现的,该扩展可能已安装或未安装在您的服务器上。最后,该函数的结果是一个二进制字符串,通常不是您所需的,因为您希望保存盐以供以后引用。我通常在使用盐之前对结果进行base64_encode编码。 - Guss
@Guss - 这些是很好的观点。此外,人们需要检查盐预期的形式。例如,BCrypt不会接受二进制盐,但base64编码的盐也无效。对于密码哈希,最好使用内置函数password_hash(),它可以自动生成安全盐。 - martinstoeckli
不使用password_hash()的一个原因是我没有实现密码。我需要一个盐来实现无状态的“API密钥”样式授权协议。 - Guss
此外,password_hash() 在所有服务器上都不可用。例如,我的服务器仅支持php 5.3。只有在5.5版本之后才能使用password_hash()。 - RoboticRenaissance
@RoboticRenaissance - 这不应该是个问题,5.3.7及更高版本有一个兼容包。稍加修改即可在5.3上使用。 - martinstoeckli

3

uniqueid 不适合生成随机字符串,因为它也是基于 microtime 的。CPU 循环通常比微秒滴答短得多,这可能会导致在循环内给定变量的可能一致性。将第二个参数“熵”设置为 true,

 uniqid('', true)

将提供更高的随机性。

要获得一个与大多数字符集兼容的随机字符串,可以对mcrypt_create_iv函数的初始化向量应用base64编码:mcrypt

$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会增加大小,这在上述中已经说明。


1

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


0

看起来这个问题已经有了一个被接受的答案,但在一番研究和阅读后,我想额外指出如果你不想把所有的鸡蛋放在一个篮子里,你可能需要增加一些安全性。

我建议不要仅仅依赖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/


-2

uniqid() 对于这个目的应该是可以的。


1
我也使用uniqid()函数,结合一些快速可计算的种子值(例如建议使用微秒时间戳)。虽然没有进行过加密分析,但似乎符合要求。 - Boldewyn

-7

我认为microtime()就足够了。

奇怪的是,我仍然因为这个答案而被踩。

尽管我得到的唯一解释是microtime是可预测的。
对我来说,这听起来很奇怪,因为盐总是被认为是公开已知的 - 所以根本没有预测的用处。


1
不是真的。不仅非常容易预测(它是当前时间),而且我也无法从中获取32个字节。 - qster
4
你可以将它用作随机数生成器,并获取任意数量的字节。这不是当前时间,你将它与time()搞混了。微秒确实变化得非常快。 而它只是盐,你不需要太高的安全性。 - Your Common Sense
请运行echo microtime()通过一个for循环(10个循环就可以了),看看你有多错... - Mike S
有趣的是,有些评论甚至在一行中可能存在矛盾之处:“这是当前时间戳,但我无法从中获取32个字节”。我一直以为Unix时间戳是一个精确的32位数字。 - Your Common Sense
4
@YourCommonSense: bits不是字节。 - datashaman

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接