我正在处理一个使用FOSUserBundle
进行用户认证的现有的Symfony
2.8网站应用程序项目。除了Web前端之外,用户还可以使用不同的智能手机客户端通过REST API连接到Web应用程序。因此,用户需要在直接登录Web应用程序和通过REST API连接时进行身份验证。
直到最新的FOSUserBundle
更新之一,bcrypt
密码哈希值和使用的salt
存储在数据库中。
使用REST API连接时,盐值被传输到客户端以使用相同的盐值本地哈希密码。然后将哈希密码发送回Web应用程序进行身份验证。
我知道发送哈希密码而不是明文并没有增加(很多)额外的安全性,因为只能使用HTTPS进行通信。但是这是客户端工作的方式:他们需要盐来生成哈希密码。我可以在未来更新客户端,但现在这只是他们工作的方式。
问题:FOSUserBundle
哈希密码的方式已更改:考虑到手动设置盐可能不够安全,而是让PHP自动生成盐(在PHP 7中甚至不可能手动设置盐),不再支持手动盐。
直接登录Web应用程序时没有问题,但由于REST客户端仍然需要盐,因此此更新会破坏REST连接。
是否有任何方法可以将这两种方法结合起来?让PHP自动创建盐,提取并将该盐发送给客户端?
据我所知,盐与哈希存储在同一字符串中:
然而,仅复制哈希字符串中的21个字符并将其发送到客户端是不起作用的。似乎这些21个字符足以测试/验证密码,但无法重新创建哈希。这是正确的吗?
那么,是否有使用PHPpassword_hash
而不设置盐,并同时了解所使用的盐的解决方案?
编辑1:
回答@RiggsFolly的问题:MD5在任何时候都没有使用过。 bcrypt
/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
password_hash
函数如果不指定盐值,每次调用都会随机生成一个盐值,因此会为相同的密码创建一个新的哈希值。
编辑2:
正如编辑1中所示,使用32个字符的盐值将导致哈希字符串只包含盐值的前21个字符。然而,由于长度太短而无法用于重新创建相同的哈希值。
但是,如果前缀用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填充到32个字符的长度。
- 将此盐传递给客户端。
有人可以确认这是一个真正的解决方案,而不仅仅是一些巧合吗?
password_hash()
中使用自己的SALT已被弃用。 - RiggsFolly