我想知道此功能(部分取自〜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移植了(可争论)。

我们应该在这里提出几个问题:

不好吗?

简短的答案是“不,这还不错”。它提供了很好的安全性。如果您现在对此有代码,那么我就不会着急将其删除。适用于大多数用途。但是话虽如此,如果您开始一个我不会选择的新项目,那么还有更好的选择。

有哪些弱点?

  • 相对于 pbkdf2 :phpass算法使用 hash() ,而pbkdf2()使用 hash_hmac() 。现在,HMAC内部为每个调用运行2个散列,但是PHP实现仅花费对hash()的单个调用执行的1.6倍(C不好吗?)。因此,我们在hash_hmac执行2个散列所需的时间中,有62%的时间从hash()获得2个散列。

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

    因此,当使用相同的原语(在这种情况下为pbkdf2)时,37.5%大约比phpassmd5。但是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(以及md5phpass)大得多的事实,在一定程度上说明了bcrypt的更强本质。

  • 应该避免

    对于新项目,绝对是。不是说它很坏。不是。事实证明,那里有更强大的算法(数量级)。那么为什么不使用它们呢?

    为了进一步证明bcrypt更强,请查看Slides from Password13 (PDF),它启动了25个GPU集群来破解密码哈希。以下是相关结果:
  • md5($password)
  • 每秒1800亿次猜测
  • 9.4小时-所有可能的8个字符的密码
  • sha1($password)
  • 每秒61亿次猜测
  • 27小时-所有可能的8个字符的密码
  • md5crypt(与phpass非常相似,费用为10):
  • 每秒7700万个猜测
  • 2.5年-所有可能的8个字符的密码
  • bcrypt,费用为5
  • 每秒7万次猜测
  • 2700年-所有可能的8个字符的密码

  • 注意:所有可能的8个字符的密码都使用94个字符集:
    a-zA-Z0-9~`!@#$%^&*()_+-={}|[]\:";'<>,.?/
    

    底线

    因此,如果您要编写新代码,请毫无疑问使用bcrypt。如果现在正在生产中有phpasspbkdf2,则可能要升级,但这并不是明确的“您非常脆弱”。

    关于php - 这在PHP中是一个很好的哈希密码功能吗?如果没有,为什么不呢?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/16042128/

    10-09 12:29