这是一个很好的哈希密码函数在PHP中? 如果不是,为什么不呢?

我想知道这个功能是否足够好(部分取自一个大约2年的phpBB版本)。

如果不是,为什么?
你将如何改变它(使现有用户无缝过渡)?

hash_pwd()的结果将保存在数据库中。

function hash_pwd($password)
{
    $itoa64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';

    $random_state = $this->unique_id();
    $random = '';
    $count = 6;

    if (($fh = @fopen('/dev/urandom', 'rb')))
    {
        $random = fread($fh, $count);
        fclose($fh);
    }

    if (strlen($random) < $count)
    {
        $random = '';

        for ($i = 0; $i < $count; $i += 16)
        {
            $random_state = md5($this->unique_id() . $random_state);
            $random .= pack('H*', md5($random_state));
        }
        $random = substr($random, 0, $count);
    }

    $hash = $this->_hash_crypt_private($password, $this->_hash_gensalt_private($random, $itoa64), $itoa64);

    if (strlen($hash) == 34)
    {
        return $hash;
    }

    return false;
}


function unique_id()
{
    $val = microtime();
    $val = md5($val);

    return substr($val, 4, 16);
}

function _hash_crypt_private($password, $setting, &$itoa64)
{
    $output = '*';

    // Check for correct hash
    if (substr($setting, 0, 3) != '$H$')
    {
        return $output;
    }

    $count_log2 = strpos($itoa64, $setting[3]);

    if ($count_log2 < 7 || $count_log2 > 30)
    {
        return $output;
    }

    $count = 1 << $count_log2;
    $salt = substr($setting, 4, 8);

    if (strlen($salt) != 8)
    {
        return $output;
    }

    /**
    * We're kind of forced to use MD5 here since it's the only
    * cryptographic primitive available in all versions of PHP
    * currently in use.  To implement our own low-level crypto
    * in PHP would result in much worse performance and
    * consequently in lower iteration counts and hashes that are
    * quicker to crack (by non-PHP code).
    */
    if (PHP_VERSION >= 5)
    {
        $hash = md5($salt . $password, true);
        do
        {
            $hash = md5($hash . $password, true);
        }
        while (--$count);
    }
    else
    {
        $hash = pack('H*', md5($salt . $password));
        do
        {
            $hash = pack('H*', md5($hash . $password));
        }
        while (--$count);
    }

    $output = substr($setting, 0, 12);
    $output .= $this->_hash_encode64($hash, 16, $itoa64);

    return $output;
}

function _hash_gensalt_private($input, &$itoa64, $iteration_count_log2 = 6)
{
    if ($iteration_count_log2 < 4 || $iteration_count_log2 > 31)
    {
        $iteration_count_log2 = 8;
    }

    $output = '$H$';
    $output .= $itoa64[min($iteration_count_log2 + ((PHP_VERSION >= 5) ? 5 : 3), 30)];
    $output .= $this->_hash_encode64($input, 6, $itoa64);

    return $output;
}

function _hash_encode64($input, $count, &$itoa64)
{
    $output = '';
    $i = 0;

    do
    {
        $value = ord($input[$i++]);
        $output .= $itoa64[$value & 0x3f];

        if ($i < $count)
        {
            $value |= ord($input[$i]) << 8;
        }

        $output .= $itoa64[($value >> 6) & 0x3f];

        if ($i++ >= $count)
        {
            break;
        }

        if ($i < $count)
        {
            $value |= ord($input[$i]) << 16;
        }

        $output .= $itoa64[($value >> 12) & 0x3f];

        if ($i++ >= $count)
        {
            break;
        }

        $output .= $itoa64[($value >> 18) & 0x3f];
    }
    while ($i < $count);

    return $output;
}

您提供的代码是PHPASS的一个端口,特别是“便携式”算法。 注意portable的资格。 这只适用于phpass库,如果你传递true作为第二个构造参数。 从现在开始的这个答案, phpass 仅指便携式算法,而不是库本身。 如果您没有明确指定portable ,库将默认进行bcrypt ...

PHPBB团队并没有自己开发(一件非常好的事情),而是直接从phpass移植(可争论)。

我们应该问几个问题:

它不好吗?

简短的答案是否定的,这并不坏。 它提供了非常好的安全性。 如果你现在有这样的代码,我不会急于下车。 它适用于大多数用途。 但有了这个说法,如果你开始一个新项目,我不会选择它,那么有更好的选择。

什么是弱点?

  • 相对于pbkdf2phpass算法使用hash() ,其中pbkdf2()使用hash_hmac() 。 现在,一个HMAC在内部为每个调用运行2次哈希,但PHP实现只需要执行大约1.6次hash()的单个调用(不是C精彩?)。 所以我们从hash_hmac中获得了2个哈希,占用了hash()执行2个哈希的62%的时间。

    那是什么意思? 那么,对于给定的运行时间, pbkdf2将运行比phpass算法 37.5%的哈希。 在给定的时间内更多的哈希==好,因为它会导致更多的计算被执行。

    因此,当使用相同的基元(本例中为md5 )时, pbkdf2大约比phpass37.5% 。 但pbkdf2也可以采取更强大的原始。 所以我们可以使用pbkdf2sha512相比phpass算法获得非常显着的优势(主要是因为sha512是一个比md5计算量更大的算法)。

    这意味着,不仅pbkdf2能够在相同的时间内生成更多的计算,还能够生成更难的计算。

    这就是说,差异不是太重要。 这是非常可衡量的, pbkdf2肯定比phpass “强”。

  • 相对于bcrypt :这是比较难以做出的。 但让我们看看它的表面。 phpass使用md5 ,并在PHP中使用循环。 pbkdf2使用任何原语(在C中)和PHP中的循环。 bcrypt在C中使用一个自定义算法(这意味着它是与任何可用哈希算法不同的算法)。 所以对蝙蝠来说, bcrypt具有显着的优势,因为算法全部在C这一事实上。这允许每单位时间进行更多“计算”。 从而使其成为更有效的慢算法(给定运行时更多的计算)。

    但是它的计算量与计算的质量一样重要。 这可能是一篇完整的研究论文,但总之归结为bcrypt在内部使用的计算比正常的散列函数难以执行。

    bcrypt更强的性质的一个例子是bcrypt使用比正常散列函数更大的内部状态。 SHA512使用512位内部状态来计算1024位的数据块。 bcrypt使用大约32kb的内部状态来计算单个块的576位。 bcrypt的内部状态比SHA512 (以及md5phpassphpass部分地说明了bcrypt的更强的性质。

  • 应该避免

    对于新项目, 绝对是 。 这并不是坏事。 事实并非如此。 这就是说,有明显更强大的算法(按数量级)。 那么为什么不使用它们呢?

    要进一步证明bcrypt更强大,请查看Passwords中的幻灯片13(PDF),该幻灯片启动了25个GPU群集来破解密码哈希。 以下是相关结果:

  • md5($password)
  • 每秒180亿个猜测
  • 9.4小时 - 所有可能的8个字符的密码
  • sha1($password)
  • 每秒钟猜测61亿
  • 27小时 - 所有可能的8个字符的密码
  • md5crypt (与phpass非常相似,费用为10):
  • 每秒77万次猜测
  • 2.5年 - 所有可能的8个字符的密码
  • bcrypt成本为5
  • 71每秒千次猜测
  • 2700年 - 所有可能的8个字符的密码
  • 注意:所有可能的8个字符的密码都使用94个字符集:

    a-zA-Z0-9~`!@#$%^&*()_+-={}|[]:";'<>,.?/
    

    底线

    所以如果你正在编写新的代码,毫无疑问使用bcrypt 。 如果您现在正在制作phpasspbkdf2 ,您可能需要升级,但这不是一个明确的“你显着弱点”。


    快速回答:

    使用bcrypt(当它准备就绪时)或来自ircmaxell的password_compat库 - 这是一个bcrypt库。

    长答案:

    对于当前的技术来说,这太复杂和过时了。 Md5应该避免,只是腌制不够好。 使用bcrypt并保存头痛。

    你可能会问自己为什么特定的图书馆? 那么相同的功能将在PHP 5.5中可用,因此您不必更改任何编码。 祝你好运,并保持简单和高效。 缓慢也适用于登录和密码的东西。

    更新1

    @ Gumbo - >没有MD5没有被破坏,但是现在和过去的MD5之类的哈希的主要用途是用于文件检查(正如你知道的那样,检查文件的内容是否可以信任而不是密码存储),因为哈希值非常大因为你不会使用Bcrypt来检查文件的内容,因为你等待了30-45秒......所以这意味着一个哈希值是专门用来快速读取的。 即使是SHA512与bcrypt相比还是完全逊色。 这就是为什么在PHP中推行像Blowfish / Bcrypt这样的强密码算法的必要性。 作为用户和程序员,我们必须扩展这些知识,即简单密码存储或低级别哈希算法不是答案 - 并且绝对不应该用于此类敏感信息。

    现在,为了让您转换到新系统,您首先会向所有用户发送一条通知,指出“为了您的安全,密码加密系统已更新........”,那么您会问他们对于一次性密码更新,一旦您进行密码更新,您将使用名为password_verify的功能,并且如果您想最大程度地控制成本比率,则使用password_needs_rehash,如果由于某种原因您选择更改与密码相关的成本。

    这不会花费大量的代码来做到这一点,因为您在密码保护中获得的收益超出了必须“重新编码”新密码系统的负面因素。

    希望这可以回答大多数问题,但是IRCmaxell的答案远不止于进一步详细介绍不同的算法! 祝你好运OP,并且非常感谢ircmaxell!

    更新2

    另外,像这样存储的密码实际上是可破解的? 怎么样? (我现在好奇)

    我的网络安全教授告诉我,任何事情和一切都被打破了。我嘲笑他。 现在我看到了他眼中的东西......好的,那是绝对正确的!

    目前加密是存储密码的最佳方法! 但是,如果你看看Scrypt,似乎是有前途的,但不被PHP支持。 没有任何东西和一切都被打破,只是一个时间问题,直到地下室里的一些“极客”会破解Bcrypt。 但现在我们很安全......就像我们对IPv4的安全性从未消失......哦,等等?......)

    希望这会回答你提出的问题,先生。 同样,只要将它放到上下文中,我的系统的费用为17,并且需要约20至30秒才能登录,但是当我将该系统呈现给我的教授和我的客户以及为什么它应该是强制性的,他们就喜欢它,感到放心,他们受到保护。


    我会去bcrypt.It是好的除了结合盐来防止彩虹桌攻击,bcrypt是一个自适应功能:随着时间的推移,迭代计数可以增加,使其更慢,所以它仍然抵抗暴力搜索攻击即使增加计算能力。

    实现:你如何在PHP中使用bcrypt来哈希密码?

    链接地址: http://www.djcxy.com/p/28925.html

    上一篇: Is this a good hashing password function in PHP? If not, why not?

    下一篇: Parsing a Vimeo ID using JavaScript?