4

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)));
}
Community
  • 1
  • 1
helloworld922
  • 10,801
  • 5
  • 48
  • 85
  • Is your concern security or speed of the hashing? – N.B. Oct 12 '12 at 09:46
  • 3. sha-512 has been known to have certain shortcuts also it is a very fast hashing algorithm which means that a simple gpu cluster and throw 10's of thousands (maybe millions) of iterations against a hash every second. 4. I use the answer to this question, it uses a good solid salt: http://stackoverflow.com/questions/4795385/how-do-you-use-bcrypt-for-hashing-passwords-in-php – Sammaye Oct 12 '12 at 09:46
  • 2. I'd think about adding something like sleep() for as long as you wish when logging in, also some code to ban the ip or sleep even more when concurrent tries were made from the same ip. – CSᵠ Oct 12 '12 at 09:48
  • @godka I am not sure how that answers 2. plus IP can be remade and spoofed so easily you cannot rely on the IP to block certain traffic, especially from botnets. – Sammaye Oct 12 '12 at 09:52
  • @N.B. I'm curious as to how the two balance out. I can always time/benchmark how long it takes to hash, but I really don't know how to figure out how safe it is. – helloworld922 Oct 12 '12 at 09:54
  • @godka I'm assuming somehow my server gets compromised and they gain access to the stored hashes. I don't see how they'd gain access to the stored hash otherwise. – helloworld922 Oct 12 '12 at 09:59
  • All hashing algorithms can be forced to produce collisions. To make up for the issue, the hashing algorithms differ in number of possible outcomes and speed. The larger the number of outcomes - the better. The slower the algorithm - the better. Why slower? Because it's not the same if you want to brute-force an algo that takes 1 second to generate 1000 hashes and algo that takes 1 minute to produce 1000 hashes. Just the sheer fact that there's trillions of combinations and that it takes some time on speedy hardware will avert anyone trying to reverse-engineer it. – N.B. Oct 12 '12 at 10:07
  • Having said that, I personally think that all 64bit hashing algorithms are sufficient for purposes of storing passwords. Naturally, salting the passwords is a must, which makes the process of reverse-engineering from hash to original password impossible in a single human life-time. – N.B. Oct 12 '12 at 10:09
  • Ok, so all things being equal I can time how long the hashing takes to give a rough estimate of the security? Or are there other factors I should take into consideration (consider just bcrypt with different load factors, same with SHA-512 with different number of iterations)? – helloworld922 Oct 12 '12 at 10:11
  • In theory yes. The longer it takes to even calculate a single part of the algorithm the longer it will take for some one else to do it and as @N.B. stated the added salt will make slow algorithms like blowfish impossible to calculate in a human lifetime. If you are truely paranoid about calculation speed I just found your best friend it seems: http://www.tarsnap.com/scrypt.html – Sammaye Oct 12 '12 at 10:13
  • You can't determine whether SHA512 is more secure than some other 64bit algorithm by estimating how long it takes to produce a single hash because you lack context. What you can do is use all the best practices and implement them - salt the passwords, use 64 bit hashing algorithm, make sure it's not one of the fast hashing algorithms like md5 because it's easy to produce collisions for them and you should be good to go. Being over-paranoid will cause over-engineering and you'll never make the feature you started with. – N.B. Oct 12 '12 at 10:35

1 Answers1

1

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.

helloworld922
  • 10,801
  • 5
  • 48
  • 85