如何使用河豚散列长密码(>72个字符)72 个字符的限制河豚和胡椒建议问题根本问题回到现实世界几乎结论至于使用“秘密”辣椒:

2022-08-30 07:47:32

上周我读了很多关于密码散列的文章,Blowfish似乎是目前最好的散列算法之一 - 但这不是这个问题的主题!

72 个字符的限制

河豚只考虑输入密码中的前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正在使用河豚又名bcrypt来存储他们的密码(https://shouldichangemypassword.com/twitter-hacked.php),并猜测什么:将您的twitter密码更改为超过72个字符的长密码,您只需输入前72个字符即可登录您的帐户。

河豚和胡椒

关于“胡椒”密码有很多不同的看法。有些人说这是不必要的,因为你必须假设秘密的胡椒串也是已知的/发布的,所以它不会增强哈希值。我有一个单独的数据库服务器,所以很可能只有数据库被泄漏,而不是恒定的pepper。

在这种情况下(胡椒没有泄露),你使基于字典的攻击更加困难(如果这不正确,请纠正我)。如果你的胡椒串也被泄漏了:不是那么糟糕 - 你仍然有盐,它和没有胡椒的哈希一样好。

所以我认为添加密码至少不是一个坏选择。

建议

我建议为超过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_verifyfalse

问题

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

优点

  • 用户无法仅通过输入前 72 个字符登录
  • 您可以在不超过字符限制的情况下添加胡椒粉
  • hash_hmac的输出可能比密码本身具有更多的熵
  • 密码由两个不同的函数进行哈希处理

缺点

  • 仅使用 64 个字符来构建河豚哈希


编辑 1:这个问题只涉及河豚/bcrypt的PHP集成。感谢您的评论!


答案 1

这里的问题基本上是熵的问题。因此,让我们开始查看那里:

每个字符的熵

每个字节的熵位数为:

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

因此,我们如何行动取决于我们期望的角色类型。

第一个问题

代码的第一个问题是,您的“pepper”哈希步骤正在输出十六进制字符(因为没有设置第四个参数 to)。hash_hmac()

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

第二个问题

但是,首先只提供一些熵。因此,您可以有效地将可能的576位减少到256位。您的哈希步骤*立即*,根据定义,至少会丢失密码中50%的可能熵。sha256256

您可以通过切换到 来部分解决此问题,其中您只能将可用熵减少约12%。但这仍然是一个不小的区别。这 12% 将排列数减少了一倍。这是一个很大的数字...这就是它减少它的因素...SHA5121.8e19

根本问题

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

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

  • 高熵随机密码

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

    对于此组,哈希函数会将其可用熵显著减小到 .bcrypt

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

  • 中等熵随机密码

    此组使用包含常见符号的密码,但没有高字节或控制字符。这些是您的可打版密码。

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

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

  • 低熵非随机密码

    这是使用可能不是随机生成的字母数字字符的组。比如圣经引用之类的东西。这些短语每个字符的熵大约为 2.3 位。

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

    让我们再说一遍。对于此类密码,预哈希肯定会显着提高安全性。

回到现实世界

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

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

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

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

几乎

实际上,这并不重要。有人猜对前72个字符的可能性是如此之低,以至于不值得担心。为什么?

好吧,假设你正在采取一个短语。如果这个人能把前72个字符弄对,他们要么真的很幸运(不太可能),要么就是一个常见的短语。如果这是一个常用短语,唯一的变量是制作它多长时间。

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

你不可贪图邻舍。你不可觊觎你邻居的妻子,或他的仆人或女仆,他的牛或驴,或任何属于你邻居的东西。

这是180个字符。第 73 个字符是第二个字符中的 。如果你猜了那么多,你可能不会停留在 ,而是继续这节经文的其余部分(因为这就是密码可能被使用的方式)。因此,您的“哈希”并没有增加太多。gneighbor'snei

顺便说一句:我绝对不主张使用圣经引用。事实上,情况恰恰相反。

结论

你不会真正帮助那些通过首先散列使用长密码的人。有些小组你绝对可以帮助。有些你肯定会受伤。

但最终,没有一个是过分重要的。我们正在处理的数字太高了。熵的差异不会太大。

你最好保持原样。你更有可能搞砸哈希(从字面上看,你已经做到了,你不是第一个或最后一个犯这个错误的人),而不是你试图阻止的攻击会发生。

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

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

至于使用“秘密”辣椒:

实际上,没有研究将一个哈希函数输入到bcrypt中。因此,目前还不清楚将“胡椒”哈希输入bcrypt是否会导致未知漏洞(我们知道这样做可能会暴露围绕冲突抵抗和前映像攻击的重大漏洞)。hash1(hash2($value))

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

基本上,在散列密码后,将整个散列输出馈送到强大的加密算法中。然后存储加密结果。

现在,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);
}

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

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


答案 2

添加密码肯定是一件好事,但让我们看看为什么。

首先,我们应该回答辣椒究竟何时有帮助的问题。胡椒只保护密码,只要它保持秘密,所以如果攻击者可以访问服务器本身,它就没有用。一个更容易的攻击是SQL注入,它允许对数据库(我们的哈希值)进行读取访问,我准备了一个SQL注入的演示,以显示它有多容易(单击下一个箭头以获取准备好的输入)。

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

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

另一点是暴力破解的范围。sha256哈希函数将返回256位输出或1.2E77组合,这对于暴力破解来说太过分了,即使对于GPU也是如此(如果我计算正确,这将在2013年在GPU上需要大约2E61年)。因此,我们没有得到使用胡椒的真正缺点。由于哈希值不是系统性的,因此您无法使用常见模式加快暴力破解的速度。

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

P.P.S 我认为您的示例存在缺陷,您无法生成具有完整密码长度的哈希值,并使用截断的哈希值进行验证。您可能打算以相同的方式应用胡椒来生成哈希和验证哈希。


推荐