Problem 1: What is the correct salt length?
All sources I found say, that the salt has a length of 22 and that it is stored together with the algorithm, the costs and the actual hash value in the result string.
如果所有消息来源都说出来,那么你不应该质疑……
没有通用的盐大小,它取决于算法和bcrypt,它是22 …虽然有一个问题.必要的大小实际上是16个字节,但实际上是Base64编码的(*).
当您对16字节的数据进行Base64编码时,将产生24个字符长度的ASCII字符串,最后2个字符无关紧要 – 当您修剪这2个不相关的字符时,它变为22.
为什么他们无关紧要?你的问题已经足够广泛了……请阅读Wikipedia page for Base64.
*实际上有一些Base64“方言”,而bcrypt使用的方言与PHP的base64_encode()不完全相同.
However, all implementations I found, use a salt with length 32. For example the FOSUserBundle used by Symfony used the following code to creat the salt:
$this->salt = base_convert(sha1(uniqid(mt_rand(), true)), 16, 36)
Since a sha1 hash is 32 chars long, the generated salt also has a length of 32. Is this just a lazy implementation, skipping the code to trim the string to a length of 22 because this is done by bcrypt it self? Or are 32 chars necessary for some reason?
该行将产生一个31个字符的字符串,而不是32个字符串,但这实际上并不相关.如果你提供一个更长的字符串,只会使用它的必要部分 – 那些最后的字符将被忽略.
你可以自己测试一下:
php > var_dump(password_hash('foo', PASSWORD_DEFAULT, ['salt' => str_repeat('a', 22).'b']));
string(60) "$2y$10$aaaaaaaaaaaaaaaaaaaaaO8Q0BjhyjLkn5wwHyGGWhEnrex6ji3Qm"
php > var_dump(password_hash('foo', PASSWORD_DEFAULT, ['salt' => str_repeat('a', 22).'c']));
string(60) "$2y$10$aaaaaaaaaaaaaaaaaaaaaO8Q0BjhyjLkn5wwHyGGWhEnrex6ji3Qm"
php > var_dump(password_hash('foo', PASSWORD_DEFAULT, ['salt' => str_repeat('a', 22).'d']));
string(60) "$2y$10$aaaaaaaaaaaaaaaaaaaaaO8Q0BjhyjLkn5wwHyGGWhEnrex6ji3Qm"
(如果使用了额外的字符,则产生的哈希值会有所不同)
我不熟悉那个FOSUserBundle,但是是的 – 看起来它只是在做一些懒惰和错误的事情.
Problem 2: Is a salt length of 22 really correct?
In the following example it seems, that only the first 21 chars of the salt are saved in the result string. Passing these 21 chars as salt to password_hash will result in an error, but padding a 0 will work:
06001
So, one needs to pass a salt with at least 22 chars to the algorithm but the 22nd chars seems to be useless. Is that correct? What is the sense of the 22nd char if it is not used at all?
它并不是真的无关紧要……用它来填充它一个’A’,你会看到不同的结果.
我无法正确地解释这一点,但它又是由Base64如何工作引起的,因为在生成的哈希中,你实际上看到类似于此的东西(伪代码):
base64_encode( base64_decode($salt) . $actualHashInBinary )
也就是说,(假设)Base64编码的盐首先被解码为原始二进制,用于创建实际的散列(再次以原始二进制形式),两者被连接,然后整个事物被Base64编码.
由于输入盐实际上是24个全长的22个相关的,我们实际上在末尾有一个不完整的块,它在原始哈希的开头完成(填充?)…
连接2个单独的Base64编码值并在Base64编码之前连接原始值是另一回事.
Problem 3: Why not specify the salt manually?
In the PHP function password_hash using a manual hash is deprecated. Instead one is encouraged to let password_hash automatically, since would be saver.
I understand that using a “weak” salt or the same salt for all passwords can lead to risks due to rainbow tables. But why is it saver to use the auto-generated salt in general?
简单地说 – 盐需要加密安全(即不可预测),并且PHP已经知道如何做到这一点,而有机会(绝大多数)你不知道.
除非你有一个实际的硬件CSPRNG(PHP尚未配置使用),你可以做的最好的事情就是让PHP自动生成盐.
然而,在这里,我们显然想要反过来(无论出于何种原因)并在此过程中降低其安全性 – 很多人都这样做.
这就是为什么不推荐使用salt选项 – 为了保护您自己. 🙂
Why is it saver to use the auto-generated salt instead of manual salt, that is generated like this:
$this->salt = base_convert(sha1(uniqid(mt_rand(), true)), 16, 36)
正如我所说,盐必须是不可预测的.在这个具体的例子中 – 使用的函数都不可预测,甚至mt_rand().
是的,mt_rand()实际上并不是随机的,尽管它的名字暗示着它.
Problem 4: Is there any replacement for password_hash that still allows the usage of a custom salt?
Due to the implementation of project I am working on, I need to control the salt, that is used to generate a password hash. This can be changed in the future, but right know it is necessary to set the salt manually. Since this feature is deprecated in password_hash, I need some alternative to generate the hash. How to do this?
你没有.
您的项目绝对没有理由说明如何生成password_hash()盐.我不知道为什么你认为这是必要的,但它100%不是 – 这没有任何意义.
虽然,最终 – 这就是为什么在删除某些东西之前实施折旧的原因.现在您知道将来会删除salt选项,并且您有足够的时间来重构您的应用程序.明智地使用它,不要尝试复制已弃用的功能.你应该在相反的方向工作 – 询问如何在不破坏你的申请的情况下将两者分开.