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

koi*_*ose 22 php security passwords cryptography password-hash

我想知道这个功能(部分取自~2岁的phpBB版本)是否足够好.

如果没有,为什么?
您将如何更改它(使现有用户无缝转换)?

hash_pwd()的结果将保存在DB中.

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;
}
Run Code Online (Sandbox Code Playgroud)

irc*_*ell 52

您给出的代码是PHPASS的端口,特别是"可移植"算法.注意资格portable.phpass如果您true作为第二个构造函数参数传递,这将仅适用于库.从现在开始的这个答案,phpass是指到便携式算法,而不是库本身.如果您没有明确指定,库将默认执行bcrypt portable...

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

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

这不好吗?

简短的回答是不,这不错.它提供了非常好的安全性.如果你现在有这方面的代码,我不会急于下车.它适用于大多数用途.但话说回来,如果你开始一个我不会选择它的新项目,还有更好的选择.

有哪些弱点?

  • 相对pbkdf2:该phpass算法使用hash(),其中pbkdf2()使用hash_hmac().现在,HMAC在内部为每个调用运行2个哈希值,但PHP实现只需要执行单次调用的大约1.6倍hash()(不是C很好吗?).所以我们hash_hmachash()执行2个哈希值的62%的时间内得到2个哈希值.

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

    所以pbkdf2大约37.5%强于phpass使用时相同的原语(md5在这种情况下).但pbkdf2也可以采取更强大的原始.因此,我们可以使用pbkdf2sha512在获得了非常显著的优势phpass算法(主要是因为sha512是一个很难算法更多的计算比md5).

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

    随着中说,差别并不过分显著.它非常可测量,pbkdf2绝对比"强" phpass.

  • 相对于bcrypt:这比较难以进行比较.但让我们来看看它的表面.phpass使用md5和PHP中的循环.pbkdf2使用任何原语(在C中)和PHP中的循环.bcrypt在C中使用自定义算法(意味着它是与任何可用哈希不同的算法).因此,对于蝙蝠的权利而言bcrypt,该算法全部在C中具有显着优势.这允许每单位时间更多的"计算".从而使其成为更有效的慢速算法(在给定的运行时间内进行更多计算).

    但同样重要的是它的计算量是计算的质量.这可能是一篇完整的研究论文,但简而言之,它归结为这样一个事实:bcrypt在内部使用的计算比普通的哈希函数要难得多.

    更强大的本质的一个例子bcryptbcrypt使用比普通散列函数更大的内部状态的事实.SHA512使用512位内部状态来计算1024位的块.bcrypt相反,使用大约32kb的内部状态来计算单个576位的块.事实上,bcrypt内部状态比SHA512(md5phpass)更大,部分地说明了它的强大本质bcrypt.

应该避免

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

有关如何bcrypt更强大的进一步证明,请查看Password13(PDF)中幻灯片,幻灯片已启动25 GPU集群以破解密码哈希值.以下是相关结果:

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

注意:所有可能的8个字符密码都使用94个字符集:

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

底线

因此,如果您正在编写新代码,请毫无疑问使用bcrypt.如果你现在有phpasspbkdf2正在制作,你可能想升级,但它并不是一个明确的"你很容易受到攻击".


Rix*_*azi 19

快速回答:

使用bcrypt(当它准备好时)或来自ircmaxell的password_compat库 - 它是一个bcrypt库.

答案很长:

对于当前的技术而言,这太复杂且过时.应该避免Md5,只是盐腌不够好.使用bcrypt并避免头痛.

你可能会问自己为什么那个特定的库?那么相同的功能将在PHP 5.5中提供,因此您不必更改任何编码.祝你好运,并保持简单高效.登录和密码的东西也很慢.

更新1

@Gumbo - >没有MD5没有被破坏,但现在和过去像MD5这样的哈希的主要目的是用于文件检查(如你所知,检查文件的内容是否可以信任而不是密码存储),因为哈希是非常的快速解密,因为你不会使用Bcrypt来检查文件的内容,因为你等待30-45秒......所以这意味着哈希特意是要快速读取.与bcrypt相比,甚至SHA512仍然完全劣质.这就是为什么必须在PHP中推动像Blowfish/Bcrypt这样的强密码算法.我们作为用户和程序员都必须扩展普通密码存储或低级散列算法不是答案的知识 - 绝不应该用于这些敏感信息.

现在向OP转换到新系统,您首先会向所有用户发送通知,声明"为​​了您的安全,密码加密系统已更新........",那么您会问他们对于一次性密码更新,一旦你进行密码更新,你将使用名为password_verify的函数,如果你想最大程度地控制你的成本比率,你可以使用password_needs_rehash,如果由于某种原因你选择更改与密码相关的成本.

这样做不会占用大量代码,因为在密码保护中你会获得的收益会影响必须"重新编码"新密码系统的负面影响.

希望这可以解答大多数问题,但是IRCmaxell的答案就是在不同的算法上进一步详细说明!祝你好运OP,非常感谢ircmaxell!

更新2

此外,这样存储的密码是否真的可以破解?怎么样?(我现在好奇)

任何事情都被打破了我的网络安全教授告诉我的事情......我嘲笑他.现在我看到了他眼中的东西......好吧......这绝对是真的!

Bcrypt CURRENTLY是存储密码的最佳方法!但是,如果你看看Scrypt似乎有希望但PHP不支持.尽管如此,一切都被打破,只是时间问题,直到地下室的一些"极客"才会破解Bcrypt.但是现在我们很安全..就像我们安全,IPv4永远不会耗尽......等等吗?...;)

希望这能解决你提出的问题,先生.也只是把它放到上下文我的系统是17的成本,登录需要大约20 - 30秒,但当我向我的教授和我的客户介绍系统时,为什么它应该是强制性的,他们喜欢它和我们感到放心,他们受到了保护.

  • @koichirose*所有密码都是"可破解的"*.真正的问题是如何轻松?即使是混淆,因为PHPBB依赖于设计为快速运行的md5,并且这种实现可以很容易地并行化.即使是强有力的密码也可以在几小时内被动机的黑客和几千美元的商品硬件强制执行.之所以经常推荐使用bcrypt是因为它很慢,并且很难(如果不是不可能的话)并行化.请参阅:http://security.stackexchange.com/questions/211/how-to-securely-hash-passwords (2认同)
  • @RixhersAjazi我认为PeeHaa指的是[preimage和碰撞攻击](http://en.wikipedia.org/wiki/MD5#Security),MD5在用于签名或完整性检查角色时容易受到攻击......你刚刚提到的正是md5应该*不被使用的地方(因为它的密码被破坏了)... (2认同)