php - 如何使用河豚散列长密码(> 72个字符)

上周我读了很多关于密码哈希和Blowfish的文章,似乎是(现在)最好的哈希算法之一 - 但这不是这个问题的主题!

72个字符的限制

Blowfish只考虑输入密码中的前72个字符:

<?php
$password = "Wow. This is a super secret and super, super long password. Let's add some special ch4r4ct3rs a#d everything is fine :)";
$hash = password_hash($password, PASSWORD_BCRYPT);
var_dump($password);

$input = substr($password, 0, 72);
var_dump($input);

var_dump(password_verify($input, $hash));
?>

输出是:

string(119) "Wow. This is a super secret and super, super long password. Let's add some special ch4r4ct3rs a#d everything is fine :)"
string(72) "Wow. This is a super secret and super, super long password. Let's add so"
bool(true)

正如您所看到的,只有前72个字符很重要。 Twitter正在使用blowfish aka bcrypt来存储他们的密码([https://shouldichangemypassword.com/twitter-hacked.php]]并猜测:将你的Twitter密码更改为超过72个字符的长密码,你可以登录到你的 帐户只输入前72个字符。

河豚和胡椒

关于“胡椒”密码有很多不同的意见。 有些人说这是不必要的,因为你必须假设秘密胡椒串也是已知/已发布的,因此它不会增强散列。 我有一个单独的数据库服务器,所以很可能只有数据库被泄露而不是常数胡椒。

在这种情况下(辣椒没有泄漏)你根据字典进行攻击更加困难(如果这不对,请纠正我)。 如果你的辣椒串也泄漏了:没那么糟糕 - 你仍然有盐,它就像没有辣椒的哈希一样受到保护。

因此,我认为密码密码至少是不错的选择。

建议

我建议为超过72个字符(和胡椒)的密码获取Blowfish哈希是:

<?php
$pepper = "foIwUVmkKGrGucNJMOkxkvcQ79iPNzP5OKlbIdGPCMTjJcDYnR";

// Generate Hash
$password = "Wow. This is a super secret and super, super long password. Let's add some special ch4r4ct3rs a#d everything is fine :)";
$password_peppered = hash_hmac('sha256', $password, $pepper);
$hash = password_hash($password_peppered, PASSWORD_BCRYPT);

// Check
$input = substr($password, 0, 72);
$input_peppered = hash_hmac('sha256', $input, $pepper);

var_dump(password_verify($input_peppered, $hash));
?>

这是基于这个问题:password_verify返回false

问题

什么是更安全的方式? 首先获取SHA-256哈希(返回64个字符)或仅考虑密码的前72个字符?

优点

  • 用户只能输入前72个字符才能登录
  • 您可以添加胡椒而不超过字符限制
  • hash_hmac的输出可能比密码本身具有更多的熵
  • 密码由两个不同的函数进行散列

缺点

  • 只有64个字符用于构建blowfish哈希


编辑1:这个问题只针对blowfish / bcrypt的PHP集成。 感谢您的评论!

Frederik Kammer asked 2019-09-10T06:56:00Z
3个解决方案
129 votes

这里的问题基本上是熵问题。 那么让我们开始寻找:

每个字符的熵

每字节熵的位数是:

  • 十六进制字符
    • 比特:4
    • 值:16
    • 熵在72个字符:288位
  • 字母数字
    • 比特:6
    • 值:62
    • 熵在72个字符:432位
  • “常见”符号
    • 比特:6.5
    • 值:94
    • 熵在72个字符:468位
  • 完整字节
    • 比特:8
    • 值:255
    • 熵在72个字符:576位

所以,我们的行为取决于我们期望的角色类型。

第一个问题

您的代码的第一个问题是您的“胡椒”哈希步骤输出十六进制字符(因为未设置第四个参数g)。

因此,通过对胡椒进行散列,您可以有效地将密码可用的最大熵减少2倍(从576到288个可能的位)。

第二个问题

但是,g首先仅提供neighbor's位熵。 所以你有效地将可能的576位削减到256位。 您的哈希步骤*立即*,非常定义失败密码中至少50%的可能熵。

您可以通过切换到g来部分解决这个问题,您只需将可用熵减少约12%。 但这仍然是一个无关紧要的差异。 那12%的排列数量减少了neighbor's。那是一个很大的数字......这就是它减少它的因素......

相关问题

根本问题是有超过72个字符的三种类型的密码。 这种风格系统对它们的影响将大不相同:

注意:从现在开始我假设我们正在比较使用g和原始输出(不是十六进制)的胡椒系统。

  • 高熵随机密码

    这些是您的用户使用密码生成器生成密码的大密钥。 它们是随机的(生成的,而不是人类选择的),并且每个字符具有高熵。 这些类型使用高字节(字符> 127)和一些控制字符。

    对于该组,您的散列函数将显着降低其可用熵为g

    让我再说一遍。 对于使用高熵,长密码的用户,您的解决方案会以可测量的数量显着降低密码强度。 (对于72个字符的密码,丢失了62位熵,对于更长的密码,则丢失了更多的熵)

  • 中等熵随机密码

    该组使用包含公共符号的密码,但没有高字节或控制字符。 这些是你的典型密码。

    对于这个组,你将略微解锁更多的熵(不创建它,但允许更多的熵适合bcrypt密码)。 当我稍微说一点时,我的意思是轻微的。 当您最大化SHA512所具有的512位时,会发生收支平衡。 因此,峰值为78个字符。

    让我再说一遍。 对于此类密码,在熵耗尽之前,您只能存储6个字符。

  • 低熵非随机密码

    这是使用可能不是随机生成的字母数字字符的组。 像圣经引用或类似的东西。 这些短语每个字符具有大约2.3位的熵。

    对于此组,您可以通过散列显着解锁更多熵(不创建它,但允许更多适合bcrypt密码输入)。 在你用完熵之前,盈亏平衡点大约是223个字符。

    让我们再说一遍。 对于这类密码,预先散列肯定会显着提高安全性。

回到现实世界

这些类型的熵计算在现实世界中并不重要。 重要的是猜测熵。 这就是攻击者可以做的直接影响。 这就是你想要最大化的东西。

虽然很少有研究猜测熵,但我想指出一些观点。

连续猜测连续72个正确字符的可能性非常低。 你更有可能赢得强力球彩票21次,而不是发生碰撞...这就是我们谈论的数字有多大。

但我们可能不会在统计上发现它。 在短语的情况下,前72个字符相同的可能性比随机密码高很多。 但它仍然很低(你更有可能赢得强力球彩票5次,基于每个字符2.3位)。

几乎

实际上,它并不重要。 有人猜测前72个字符的可能性是正确的,后者会产生显着差异,因此不值得担心。 为什么?

好吧,假设你说的是一句话。 如果这个人能够得到正确的前72个字符,那么他们要么真的很幸运(不太可能),要么就是一个常见的短语。 如果它是一个常见的短语,唯一的变量就是它需要多长时间。

我们来举个例子吧。 让我们从圣经中引用一句话(因为它是长文本的常见来源,不是出于任何其他原因):

你不应该贪图你邻居的房子。 你不应该贪图你邻居的妻子,他的男仆或女仆,他的牛或驴,或属于你邻居的任何东西。

这是180个字符。 第73个字符是第二个g中的g。如果你猜到这么多,你可能不会停留在nei,但继续使用其余的节目(因为这是密码可能被使用的方式)。 因此,你的“哈希”没有增加多少。

顺便说一句:我绝对不提倡使用圣经引用。 事实上,恰恰相反。

结论

你不是真的会帮助那些通过哈希首先使用长密码的人。 有些团体你肯定可以提供帮助。 有些你肯定会受伤。

但最终,这些都不是太重要。 我们正在处理的数字太高了。 熵的差异不会太大。

你最好离开bcrypt。 你试图阻止的攻击将会发生,你更有可能搞砸哈希(字面意思是,你已经完成了,并且你不是第一个或最后一个犯错误)。

专注于保护网站的其余部分。 并在注册时在密码框中添加密码熵计,以指示密码强度(并指示密码是否过长,用户可能希望更改密码)...

这至少是0.02美元(或者可能超过0.02美元)......

至于使用“秘密”辣椒:

实际上没有研究将一个哈希函数输入到bcrypt中。 因此,最好不清楚如果向bcrypt提供“胡扯”哈希将导致未知漏洞(我们知道做Zend\Crypt可能会暴露围绕碰撞阻力和原像攻击的重大漏洞)。

考虑到你已经在考虑存储一个密钥(“胡椒”),为什么不以一种经过充分研究和理解的方式使用它? 为什么不在存储之前加密哈希?

基本上,在对密码进行哈希处理后,将整个哈希输出提供给强加密算法。 然后存储加密结果。

现在,SQL注入攻击不会泄漏任何有用的东西,因为它们没有密码密钥。 如果密钥被泄露,攻击者并没有比使用普通哈希(这是可证明的,胡椒“预哈希”不提供的东西)更好。

注意:如果您选择这样做,请使用库。 对于PHP,我强烈推荐Zend Framework 2的Zend\Crypt包。 它实际上是我目前在这个时间点推荐的唯一一个。 它经过了强烈的审核,它为您做出了所有决定(这是一件非常好的事情)......

就像是:

use Zend\Crypt\BlockCipher;

public function createHash($password) {
    $hash = password_hash($password, PASSWORD_BCRYPT, ["cost"=>$this->cost]);

    $blockCipher = BlockCipher::factory('mcrypt', array('algo' => 'aes'));
    $blockCipher->setKey($this->key);
    return $blockCipher->encrypt($hash);
}

public function verifyHash($password, $hash) {
    $blockCipher = BlockCipher::factory('mcrypt', array('algo' => 'aes'));
    $blockCipher->setKey($this->key);
    $hash = $blockCipher->decrypt($hash);

    return password_verify($password, $hash);
}

这是有益的,因为你以很好理解和充分研究的方式使用所有算法(相对至少)。 记得:

任何人,从最无能的业余爱好者到最好的密码学家,都可以创建一个他自己无法破解的算法。

  • 布鲁斯施奈尔
ircmaxell answered 2019-09-10T07:03:50Z
5 votes

佩戴密码肯定是件好事,但让我们明白为什么。

首先,我们应该回答问题,确切的胡椒有助于。 胡椒只保护密码,只要它保密,所以如果攻击者可以访问服务器本身,那就没用了。 一个更容易的攻击是SQL注入,它允许对数据库的读访问(对我们的哈希值),我准备了一个SQL注入的演示,以显示它是多么容易(点击下一个箭头来准备输入)。

然后胡椒实际上有什么帮助? 只要胡椒保持秘密,它就可以保护弱密码免受字典攻击。 密码1234将变为类似1234-p*deDIUZeRweretWy+.O.此密码不仅更长,它还包含特殊字符,永远不会成为任何字典的一部分。

现在我们可以估计用户将使用哪些密码,可能会有更多用户输入弱密码,因为密码介于64-72个字符之间的用户(实际上这将是非常罕见的)。

另一点是暴力迫使的范围。 sha256散列函数将返回256位输出或1.2E77组合,这对于暴力强制来说太多了,即使对于GPU来说也是如此(如果我正确计算,2013年在GPU上需要大约2E61年)。 所以我们不应该使用辣椒真正的劣势。 因为哈希值不是系统的,所以无法加速使用常见模式的强制执行。

附: 据我所知,72字符限制特定于BCrypt本身的算法。 我找到的最佳答案是这个。

P.P.S我认为你的例子是有缺陷的,你不能用完整的密码长度生成哈希,并用截断的密码验证它。 您可能打算以相同的方式应用胡椒来生成散列和验证散列。

martinstoeckli answered 2019-09-10T07:04:57Z
2 votes

Bcrypt使用基于昂贵的Blowfish密钥设置算法的算法。

bcrypt的建议56字节密码限制(包括空终止字节)与Blowfish密钥的448位限制有关。 超出该限制的任何字节都不会完全混合到生成的哈希中。 因此,当您考虑这些字节对结果哈希的实际影响时,bcrypt密码的72字节绝对限制不太相关。

如果您认为您的用户通常会选择长度超过55个字节的密码,请记住,您可以随时增加密码延长次数,以便在密码表违规的情况下提高安全性(尽管与增加额外的密码表相比,这需要很多)字符)。 如果用户的访问权限非常严重以至于用户通常需要大量长密码,那么密码到期时间也应该很短,例如2周。 这意味着当黑客投入资源来破坏测试每个试用密码所涉及的工作因素以查看它是否会产生匹配的哈希值时,密码不太可能保持有效。

当然,如果密码表没有被破坏,我们应该只允许黑客在锁定用户帐户之前最多十次尝试猜测用户的55字节密码;)

如果您决定预先散列长度超过55个字节的密码,那么您应该使用SHA-384,因为它具有最大输出而不超过限制。

Phil answered 2019-09-10T07:05:52Z
translate from https://stackoverflow.com:/questions/16594613/how-to-hash-long-passwords-72-characters-with-blowfish