我正在处理现有的 Symfony
2.8 网络应用程序项目,该项目使用 FOSUserBundle
进行用户身份验证。
除了 Web 前端,用户还可以使用不同的智能手机客户端通过 REST API 连接到 Web 应用程序。因此,用户在直接登录 Web 应用程序和连接 REST API 时都需要进行身份验证。
直到最新的 FOSUserBundle
更新了一个 bcrypt
密码散列和存储在数据库中的使用过的 salt
。
当使用 REST API 连接时,salt 被传输到客户端以使用相同的 salt 在本地散列密码。散列密码然后发送回网络应用程序进行身份验证。
我知道发送散列密码而不是纯文本不会增加(很多)额外的安全性,因为只能使用 HTTPS 进行通信。然而,这是客户端的工作方式:他们需要盐来生成散列密码。我可以在未来更新客户端,但现在这就是工作方式。
问题:
他们以 FOSUserBundle
哈希的方式更改了密码:因为不手动指定 salt 而是让 PHP 自动生成 salt 被认为更省事(在 PHP 7 中,甚至无法手动设置盐),不再支持手动加盐。
直接登录 Web 应用程序时没有问题,但由于 REST 客户端仍然需要加盐,因此此更新会中断 REST 连接。
有什么方法可以结合这两种方法吗?让 PHP 自动创建盐,提取盐并将其发送给客户端?
据我所知,盐与散列存储在同一个字符串中:
但是,简单地从散列字符串中复制 21 个字符的盐并将其发送给客户端是行不通的。 这 21 个字符似乎足以测试/验证密码,但不足以重新创建哈希。这是正确的吗?
那么,有没有什么解决方案可以在不设置salt的情况下使用PHP password_hash
,同时了解使用的salt?
编辑 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
如果未指定盐,password_hash
将为相同的密码创建一个新的哈希值。这是因为,salt 将随机创建,这在每次调用时都会有所不同。
编辑 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个字符的长度
- 将此盐传递给客户
谁能证实,这是一个真正的解决方案,而不仅仅是巧合?
最佳答案
您似乎误解了密码散列和盐分的工作原理。
盐永远不会发送给客户端。它仅在创建密码时生成(或手动指定)一次。其目的是随机化哈希函数的输出,以便当数据库落入坏人之手时,无法通过将输出与彩虹表进行比较来获取用户密码。
当用户使用他的密码登录时,密码从客户端发送到服务器,未经哈希处理(但通常通过 https)。密码比较函数然后获取存储的散列+密码,从中获取盐,将盐附加到用户输入,计算散列,然后将其与数据库中的散列进行比较。
也许您正在进行的项目对盐的实现不佳。事实上,不鼓励使用手动盐的原因之一是为了防止这样的事情发生。
So a solution could be:
let FOSUserBundle use password_hash to create the hash without manually specifying a salt.
extract the salt from the result string and pad it with 0 to a length of 32 chars
pass this salt to the client
Can anyone confirm, that this a real solution and not just some coincidence ?
这不是一个好的解决方案。唯一好的方法是确保以正确的方式实现密码散列,这样您就不必多次生成盐。
关于php - 了解盐是如何在 bcrypt password_hash 中生成/使用的,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40991000/