我正在开发一个使用Symfony
进行用户身份验证的现有FOSUserBundle
2.8网络应用程序项目。
除了Web前端,用户还可以使用不同的智能手机客户端使用REST API连接到Web应用程序。因此,在直接登录Web应用程序和连接REST API的原因时,用户都需要进行身份验证。
直到最新FOSUserBundle
之一更新bcrypt
密码哈希和存储在数据库中的已使用salt
。
使用REST API进行连接时,会将salt传输到客户端,以使用相同的salt对密码进行本地哈希处理。散列密码将发送回Web应用程序进行身份验证。
我知道发送散列密码而不是纯文本不会增加(很多)额外的安全性,因为只能使用HTTPS进行通信。然而,这是客户端工作的方式:他们需要salt来生成哈希密码。我可以在将来更新客户端,但现在这只是工作方式。
问题:
他们的方式FOSUserBundle
哈希密码已经改变:因为不考虑手动指定盐而是让PHP自动生成盐(在PHP 7中甚至不可能手动设置盐),不再支持手动盐。
直接登录Web应用程序时没有问题,但由于REST客户端仍需要salt,因此此更新会破坏REST连接。
有没有办法合并这两种方法?让PHP自动创建盐,提取并将此盐发送给客户端?
据我所知,salt与哈希存储在同一个字符串中:
但是,只需从哈希字符串中复制21个char盐并将这些发送到客户端就不起作用了。 似乎这21个字符足以测试/验证密码,但不能重新创建哈希。这是正确的吗?
那么,是否有任何解决方案可以在不设置盐的情况下使用PHP password_hash
,并且同时了解使用过的盐?
编辑1:
回答@RiggsFolly问题:MD5在任何时候都没有使用过。 不正确,bcryp
/ password_hash
不会两次创建相同的哈希值。如果密码和盐都相同,它会这样做:
$s = 'password';
$salt = 'salt5678901234567890123456789012';
$options['salt'] = $salt;
$h1 = password_hash($s,PASSWORD_BCRYPT,$options);
$h2 = password_hash($s,PASSWORD_BCRYPT,$options);
echo $h1 . PHP_EOL;
echo $h2 . PHP_EOL;
结果:
$2y$10$salt56789012345678901uTWNlUnhu5K/xBrtKYTo7oDy8zMr/csu
$2y$10$salt56789012345678901uTWNlUnhu5K/xBrtKYTo7oDy8zMr/csu
如果未指定salt, password_hash
将为相同的密码创建新哈希。这是因为,盐将随机创建,而不是每次调用时产生不同的盐。
编辑2:
正如编辑1中所见,使用具有32个字符的盐将导致字符串仅包含盐的前21个字符。但是,这个salt-prefix不能用于重新创建相同的哈希,因为它太短而无法被接受。
但是,如果前缀填充为0,则似乎有效:
$s = 'password';
$salt = 'salt5678901234567890123456789012';
$salt_prefix = 'salt5678901234567890100000000000';
$h1 = password_hash($s, PASSWORD_BCRYPT, array('salt' => $salt));
$h2 = password_hash($s, PASSWORD_BCRYPT, array('salt' => $salt_prefix));
echo $h1 . PHP_EOL;
echo $h2 . PHP_EOL;
所以解决方案可能是:
FOSUserBundle
使用password_hash
创建哈希,无需手动指定盐。任何人都可以确认,这是一个真正的解决方案而不仅仅是巧合吗?
答案 0 :(得分:0)
如http://us2.php.net/crypt所述,盐是22个字符,而不是21。
reloadData()
salt5678901234567890123456789012盐中的最后2个盐变成了你,只是隐窝河豚的一些魔力。
答案 1 :(得分:0)
让客户提供任何盐。或者服务器提供从未使用过的随机盐。将哈希视为原始密码。重新哈希哈希密码和其他内容。
答案 2 :(得分:0)
您似乎误解了密码哈希和盐应该如何工作。
盐永远不会发送给客户。创建密码时仅生成(或手动指定)一次。它的目的是随机化散列函数的输出,这样当数据库落入坏人手中时,就不可能通过将输出与彩虹表进行比较来获取用户密码。
当用户使用其密码登录时,密码将从客户端发送到服务器(未通过https)。密码比较函数然后获取存储的哈希+密码,从中获取盐,将盐附加到用户输入,计算哈希值,然后将其与数据库中的哈希值进行比较。
也许你所参与的项目盐的实施不好。事实上,不鼓励使用手工盐的原因之一是防止这样的事情。
所以解决方案可能是:
让FOSUserBundle使用password_hash来创建没有的哈希 手动指定盐。
从结果字符串中提取salt并将其填充为0到一个长度 32个字符
将此盐传递给客户
任何人都可以确认,这是一个真正的解决方案而不仅仅是一些 巧合?
这不是一个好的解决方案。唯一的好方法是确保以正确的方式实现密码哈希,这样您就不必多次生成盐。