密码和密码存储问题

Related questions:

Am I using PHP's crypt() function correctly?

Password storage hash with SHA-512 or crypt() with blowfish (bcrypt)

I'm trying to figure out how I should safely store a password using PHP. After a little reading I've identified that I should use crypt() instead of hash() and that I should use either the blowfish (bcrypt) or SHA-512 algorithms, with I believe bcrypt being recommended more often, though there is significant support for SHA-512 based algorithms, too.

There are also many suggestions that my salt should be as random as possible, with many suggestions to use openssl_random_pseudo_bytes() over the core rand() and mt_rand().

My main questions are:

  1. If I choose to use bcrypt, what load factors should I consider using? I noticed that for PHP 5.5 the default load factor in the new password API is 10 so I would imagine I would want at least that value.

  2. How does the load factor correlate to password safety? From what I understand the algorithm will iterate 2^load_factor times, but I'm more interested in how this translates into safety against brute force cracking methods. What does it mean to be "safe"? Does it take 10 years to crack? 5 years? 1 year?

  3. Why should I pick bcrypt over a SHA-512 based method (or vise-versa)? I've heard that SHA-512 is designed to be a fast hashing method so over time it won't hold up as well as bcrypt. Is this true? Both methods have salt parameters which allow crypt to iterate multiple times.

  4. To the best of my knowledge, I implemented the following test code which generates a bcrypt salt. Is the recommended method? Is there a better way to do it?

_

function gen_salt($cost)
{
    return "$2y$" . $cost . "$" . str_replace('+', '.', base64_encode(openssl_random_pseudo_bytes(22)));
}

So based off of the comments I created a simple benchmark to test how long various hashing methods take.

function bcrypt_salt($cost)
{
    return "$2y$" . $cost . "$" . str_replace('+', '.', base64_encode(openssl_random_pseudo_bytes(22))) . '$';
}

function sha512_salt($cost)
{
    return "\$6\$rounds=" . $cost . "\$" . openssl_random_pseudo_bytes(16) . '$';
}

$password = "stackoverflow";
$times = 1;
echo "<p>bcrypt method</p>";
for($iters = 10; $iters < 15; ++$iters)
{
    $salt = bcrypt_salt(strval($iters));
    $pword_crypt = crypt($password, $salt);
    $start_time = microtime(true);
    for($i = 0; $i < $times; ++$i)
    {
        crypt($password, $pword_crypt);
    }
    $end_time = microtime(true);
    echo "<p> cost = $iters: " . ($end_time - $start_time) . "</p>";
}

echo "<p>SHA512 method</p>";
for($iters = 1024; $iters < 1000000; $iters *= 2)
{
    $salt = sha512_salt(strval($iters));
    $pword_crypt = crypt($password, $salt);
    $start_time = microtime(true);
    for($i = 0; $i < $times; ++$i)
    {
        crypt($password, $pword_crypt);
    }
    $end_time = microtime(true);
    echo "<p> log2(iters) = ". log($iters,2) . ": " . ($end_time - $start_time) . "</p>";
}

Benchmark results (time in seconds):

Ran on my laptop with an i5-m430:

bcrypt method

cost = 10: 0.11740303039551

cost = 11: 0.23875308036804

cost = 12: 0.46739792823792

cost = 13: 0.96053194999695

cost = 14: 1.8993430137634

SHA512 method

log2(iters) = 10: 0.0034840106964111

log2(iters) = 11: 0.0077731609344482

log2(iters) = 12: 0.014604806900024

log2(iters) = 13: 0.02855396270752

log2(iters) = 14: 0.068222999572754

log2(iters) = 15: 0.12677311897278

log2(iters) = 16: 0.24734497070312

log2(iters) = 17: 0.54663610458374

log2(iters) = 18: 1.0215079784393

log2(iters) = 19: 2.0223300457001

All things being equal, it takes a much higher number of iterations for the SHA-512 method vs. bcrypt to take the same amount of time. That being said I'm guessing that any method which takes at least a tenth of a second is more than sufficient.