当前位置: 首页 > 知识库问答 >
问题:

如何用blowfish散列长密码(>72个字符)

章誉
2023-03-14

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

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)

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

在这种情况下(pepper没有泄露),您会使基于字典的攻击更加困难(如果这是不对的,请纠正我)。如果你的胡椒串也漏了:没那么糟--你还有盐,它就像没有胡椒的杂碎一样好保护。

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

对于超过72个字符(和pepper)的密码,我的建议是:

<?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_verifyreturnfalse

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

  • 用户不能通过只输入前72个字符登录
  • 您可以在不超过字符限制的情况下添加辣椒
  • hash_hmac的输出可能比密码本身具有更多的熵
  • 密码由两个不同的函数哈希
  • 只使用64个字符构建blowfish散列

共有1个答案

安泰平
2023-03-14

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

每个字节的熵位数为:

  • 十六进制字符
    • 位:4
    • 值:16
    • 72个字符的熵:288位
      null
      null
      null

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

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

    但是,SHA256首先只提供256位熵。因此,有效地将可能的576位减少到256位。您的哈希步骤*立即*,根据非常明确的定义,丢失至少50%的可能熵在密码。

    您可以通过切换到SHA512来部分解决这个问题,在这里您只会将可用熵减少约12%。但这仍然是一个不小的差异。这12%将置换的数量减少了1.8e19的一倍。这是一个很大的数字...这就是它减少它的因素...

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

    注意:从这里开始,我假设我们与使用SHA512和原始输出(不是十六进制)的pepper系统进行比较。

    >

  • 高熵随机密码

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

    对于这个组,散列函数将显著地将它们的可用熵降低到bcrypt

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

    中熵随机密码

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

    对于这个组,您将稍微解锁更多的熵(不是创建它,而是允许更多的熵适合bcrypt密码)。我所说的微微,是指微微。当您最大限度地耗尽SHA512的512位时,就会出现盈亏平衡。因此,峰值在78个字符。

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

    低熵非随机密码

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

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

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

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

    不可贪图邻舍的房子。你不可贪图邻舍的妻,或他的男仆,婢女,牛,驴,并你邻舍的一切物。

    那是180个字符。第73个字符是第二个邻居的中的G。如果您猜到了那么多,那么您可能不会停在nei,而是继续下面的部分(因为密码可能就是这样使用的)。因此,你的“哈希”并没有添加太多。

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

    你不会真正帮助那些使用长密码先哈希的人。一些你绝对可以帮助的团体。一些你肯定会伤害的。

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

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

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

    实际上没有研究将一个哈希函数添加到Bcrypt中。因此,充其量还不清楚向bcrypt中输入“pepped”散列是否会导致未知的漏洞(我们知道执行hash1(hash2($value))可能会暴露碰撞抵抗和预映像攻击方面的重大漏洞)。

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

    基本上,在对密码进行散列后,将整个散列输出输入到一个强加密算法中。然后存储加密的结果。

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

    注意:如果选择这样做,请使用库。对于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);
    }
    

    这是有益的,因为您使用的所有算法都是很好地理解和研究的(至少相对来说)。记住:

      null

 类似资料:
  • 我的问题来自于如何用blowfish散列长密码(>72个字符) 我正在使用bcrypt(blowfish)对密码进行散列。因此,我从这个问题中发现https://security.stackexchange.com/questions/39849/does-bcrypt-have-a-maximal-password-length 它的字符限制为72。 所以,我开始考虑限制密码的最大长度,但在这些

  • 问题内容: 我需要对密码进行哈希处理以存储在数据库中。如何用Java做到这一点? 我希望使用纯文本密码,添加随机盐,然后将盐和哈希密码存储在数据库中。 然后,当用户想要登录时,我可以使用其提交的密码,从其帐户信息中添加随机盐,对其进行哈希处理,然后查看其是否等同于其帐户信息所存储的哈希密码。 问题答案: 实际上,你可以使用Java运行时内置的工具来执行此操作。Java 6中的SunJCE支持PBK

  • 我正在为拉拉维尔创建一个散列密码。现在有人告诉我使用Laravel哈希助手,但我似乎找不到它,或者我找错了方向。 如何创建laravel散列密码?在哪呢? 编辑:我知道代码是什么,但我不知道在哪里和如何使用它,所以它返回给我散列密码。如果得到散列密码,就可以手动将其插入数据库

  • 我试图完全理解密码散列,以便能够向审计员解释它。 基于我对答案的搜索,我知道函数是的包装器。在阅读预定义常量的PHP手册时,我看到它使用作为默认整数值(基本上它使用算法来散列密码)。 让我困惑的是,变量如果省略,会生成一个随机salt,并且成本将设置为。如果我提供了更高的成本(例如:),由于我没有提供salt值,它还会生成随机salt吗?我在这里感到困惑的原因是,我没有忽略,而是提供了不同的成本。

  • 控制器文件只是调用服务方法。 服务类别: 我想密码是在行中散列后验证的: 如何在散列和保存之前验证此密码? 如有任何帮助,不胜感激。提前谢了。

  • 我正在处理一个pet项目,现在我想添加用户注册支持。经过一些研究,我决定在数据库中保存哈希密码和salt,而不是原始密码。然而,我不知道这些步骤是什么,顺序是什么。以下是我的假设: 注册 客户端向服务器发送用户名和密码(https) 服务器获取密码,生成随机salt Base64对盐进行编码 用stringify salt散列密码 Base64对密码进行编码 将密码和salt保存到数据库中 登录