繁体   English   中英

了解如何在bcrypt password_hash中生成/使用salt

[英]Understanding how salt is generated/used in bcrypt password_hash

我正在使用现有的Symfony 2.8 Web应用程序项目,该项目使用FOSUserBundle进行用户身份验证。

除了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创建哈希, 而无需手动指定salt。
  • 从结果字符串中提取salt并将其填充为0到32个字符的长度
  • 将这种盐传递给客户

任何人都可以证实,这是一个真正的解决方案,而不仅仅是一些巧合吗?

http://us2.php.net/crypt所述 ,盐是22个字符而不是21个字符。

<?php
$key = 'password';
$hash = password_hash($key, PASSWORD_BCRYPT);
$salt = substr($hash, 7, 22);
$rehash = password_hash($key, PASSWORD_BCRYPT, ['salt' => $salt]);
if ($hash == $rehash) {
  echo 'ok', PHP_EOL;
}

salt5678901234567890123456789012盐中的最后2个改变为u只是隐窝河豚的一些魔力。

让客户提供任何盐。 或者服务器提供从未使用过的随机盐。 将哈希视为原始密码。 重新哈希哈希密码和休息的东西。

您似乎误解了密码散列和盐应该如何工作。

盐永远不会发送给客户。 创建密码时仅生成(或手动指定)一次。 它的目的是随机化散列函数的输出,这样当数据库落入坏人之手时,就不可能通过将输出与彩虹表进行比较来获取用户密码。

当用户使用他的密码登录时,密码是从客户端发送到服务器的,而不是哈希(但通常是通过https)。 密码比较函数然后获取存储的哈希+密码,从中获取盐,将盐附加到用户输入,计算哈希值,然后将其与数据库中的哈希值进行比较。

也许你正在进行的项目有很糟糕的盐实施。 事实上,不鼓励使用手工盐的原因之一是防止这样的事情。

所以解决方案可能是:

让FOSUserBundle使用password_hash创建哈希,而无需手动指定salt。

从结果字符串中提取salt并将其填充为0到32个字符的长度

将这种盐传递给客户

任何人都可以证实,这是一个真正的解决方案,而不仅仅是一些巧合吗?

这不是一个好的解决方案。 唯一的好方法是确保以正确的方式实现密码哈希,这样您就不必多次生成盐。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM