如何正确存储PBKDF2密码哈希

Ale*_*lex 6 php encryption passwords hash pbkdf2

我一直在研究如何正确地散列/加密密码并将其存储在数据库中.我知道Salt和Hashing所以我环顾四周,PBKDF2似乎是一个不错的选择.所以我发现这个网站提供了一个很好的教程,以及适用于PHP的PBKDF2(这是我用于我的网站).

所以我设置我的网站使用这些函数来生成/创建密码,但正如您在以下代码中看到的那样:

function create_hash($password) {
// format: algorithm:iterations:salt:hash
$salt = base64_encode(mcrypt_create_iv(PBKDF2_SALT_BYTES, MCRYPT_DEV_URANDOM));
return PBKDF2_HASH_ALGORITHM . ":" . PBKDF2_ITERATIONS . ":" .  $salt . ":" .
    base64_encode(pbkdf2(
        PBKDF2_HASH_ALGORITHM,
        $password,
        $salt,
        PBKDF2_ITERATIONS,
        PBKDF2_HASH_BYTES,
        true
    )); }
Run Code Online (Sandbox Code Playgroud)

salt在create_hash函数中生成,并存储在生成的哈希中,最终看起来像sha256:1000:salt:hashed_pa​​ssword.这是我必须存储在我的数据库中,并且由于salt包含在生成的哈希中,我不需要将它添加到我的数据库中.但是,在用这个生成一些测试用户之后,我想知道在数据库中使用哈希密码进行PBKDF2设置实际上是一件好事.他们的方式我的新手自己看到它是一个黑客,破解我的数据库后,会看到这些sha256:1000:盐:密码的东西,并弄清楚每个部分将代表什么,这将有助于他在他的尝试,不?

所以我修改了一下我生成并存储在我的数据库中的外部盐,并在通过PBKDF2运行之前在密码中包含salt.然后我做同样的事情来比较给定的密码和我在我的数据库中用于登录,它的工作原理.我唯一担心的是,使用128位盐,生成的密码哈希长度只有50个字符,这对我来说似乎不对.

这是我目前的代码:

define("PBKDF2_HASH_ALGORITHM", "sha256");
define("PBKDF2_ITERATIONS", 10000);
define("PBKDF2_SALT_BYTES", 128);
define("PBKDF2_HASH_BYTES", 24);

define("HASH_SECTIONS", 4);
define("HASH_ALGORITHM_INDEX", 0);
define("HASH_ITERATION_INDEX", 1);
define("HASH_SALT_INDEX", 2);
define("HASH_PBKDF2_INDEX", 3);

function create_hash($password, $salt)
{
    // format: salthash
    return  
        base64_encode(pbkdf2(
            PBKDF2_HASH_ALGORITHM,
            $password,
            $salt,
            PBKDF2_ITERATIONS,
            PBKDF2_HASH_BYTES,
            true
        ));
}

function validate_password($password, $salt, $good_hash)
{
    $pbkdf2 = base64_decode($good_hash);
    return slow_equals(
        $pbkdf2,
        pbkdf2(
            PBKDF2_HASH_ALGORITHM,
            $password,
            $salt,
            PBKDF2_ITERATIONS,
            PBKDF2_HASH_BYTES,
            true
        )
    );
}

// Compares two strings $a and $b in length-constant time.
function slow_equals($a, $b)
{
    $diff = strlen($a) ^ strlen($b);
    for($i = 0; $i < strlen($a) && $i < strlen($b); $i++)
    {
        $diff |= ord($a[$i]) ^ ord($b[$i]);
    }
    return $diff === 0; 
}

function pbkdf2($algorithm, $password, $salt, $count, $key_length, $raw_output = false)
{
    $algorithm = strtolower($algorithm);
    if(!in_array($algorithm, hash_algos(), true))
        die('PBKDF2 ERROR: Invalid hash algorithm.');
    if($count <= 0 || $key_length <= 0)
        die('PBKDF2 ERROR: Invalid parameters.');

    $hash_length = strlen(hash($algorithm, "", true));
    $block_count = ceil($key_length / $hash_length);

    $output = "";
    for($i = 1; $i <= $block_count; $i++) {
        // $i encoded as 4 bytes, big endian.
        $last = $salt . pack("N", $i);
        // first iteration
        $last = $xorsum = hash_hmac($algorithm, $last, $password, true);
        // perform the other $count - 1 iterations
        for ($j = 1; $j < $count; $j++) {
            $xorsum ^= ($last = hash_hmac($algorithm, $last, $password, true));
        }
        $output .= $xorsum;
    }

    if($raw_output)
        return substr($output, 0, $key_length);
    else
        return bin2hex(substr($output, 0, $key_length));
}
Run Code Online (Sandbox Code Playgroud)

我对这种密码保存格式的关注是否有意义?或者它只是相同的事情,因为通过在我的数据库中使用我的盐,一旦它被破解,黑客仍然可以通过暴力破解?

谢谢,抱歉这个长期的问题.

Ale*_*lex 2

用@Romain 的评论回答我自己的问题。

请参阅这些答案,以便更好地理解此处隐藏盐是多么无用,此处使用 bcrypt(因为在您的链接中未给出实现) ,以及此处如何使用哈希来保护密码的最佳实践。最后,永远不要低估你的对手!

另外,(因为这有点旧),bcrypt 确实比 pbkdf2 “更好”,但 scrypt 甚至更好!