Hashing Passwords in PHP Using md5 vs sha256 vs bcrypt vs …

Many people have had questions about how to properly store passwords in the database. Most of us have learned that it is a really bad idea to store them in plaintext.

Instead of storing them in plaintext we do store them with a one-way hash, such as md5, sha256 or bcrypt. This way even if someone gets a copy of your password database it will become harder to figure out a users password. A one way hash in this examples is a (seemingly) one to one mapping from a string to another string. In theory you can’t run a function on the hashed password to get back the plaintext password. So if someone gets your database than, no problem?

Well yes and no; a baddie can still try running a huge list of possible passwords and see if they match any of hashes in the database. So the faster a hash function is to calculate the worse it is for security, because the baddie can try more passwords faster and cheaply.

MD5 and SHA256 (or any of the sha* functions) where designed to be really, really fast. So a baddie can run many possible passwords through them in order to check. BCRYPT, which is a variant of the blowfish encryption algorithm, can be made arbitrarily difficult. Put it another way, the number of CPU cycles for MD5 and SHA256 are very few because they are meant to be fast, bcrypt can be made to require a huge amount of CPU cycles to calculate the result.

Bcrypt is better because it can be made to take up more CPU cycles. END OF STORY!

Or is it… Well in reality all we are REALLY trying to do is make a trade-off between the number of CPU cycles we are willing to give up in-order to make the users passwords more secure vs how many CPU cycles the baddies are willing to give up in order to crack the password.

Sidenote: adding a random salt doesn’t in practice significantly increase the number of CPU cycles, it only makes it so the baddies must recalculate hashes for common passwords. Its never a bad idea to hash, its just not terribly effective either.

Bcrypt would be an obvious choice if it was more portable, however as of the writing of this post bcrypt is not built into most database servers or PHP itself. MD5 and SHA256 are available in most servers and languages. Loading an external bcrypt library takes a bit of CPU cycles that could be better used in running the hash function and you have to port all that library. Loading the library doesn’t make the password more secure, only running the algorithm does. So if we want to make things simple just use md5 iteratively.

Here is an example of a hardening function that iterates the md5 algorithm many times in order to use up more cpu cycles, change $md5_iters so that it takes about .1 seconds to run on your production server. Higher number means more security but also takes away cpu cycles from other things the server needs to do.

Hardening Password Function (with iterative MD5)

function harden_password($clear_text_password,$random_salt){
  // a non-round number, works similar to a salt as well as increasing
  // number of cpu cycles, set it and forget it, number of iters
  // should be increased until this function takes about .1 seconds on
  // your production server.
  $md5_iters = 81378;
 
  $hashed_password = md5($clear_text_password); // first hash
  // iterate the hash
  for($ii=0; $ii<$md5_iters; $ii++){
    $hashed_password = md5($hashed_password . $random_salt);
  }
  return $hashed_password;
}

Here is an example of how to use the hardening function to create hashes and validate passwords

Example of Using Hardening Function

$clear_text_password = '123456789'; // an "average" length password string
// a 'random' salt this should be unique for EACH user, but it is also
// a set it and forget it variable for that user
$random_salt = md5(mt_rand(0,1<<30)); 
 
function harden_password($clear_text_password,$random_salt){
  // a non-round number, works similar to a salt as well as increasing
  // number of cpu cycles, set it and forget it, number of iters
  // should be increased until this function takes about .1 seconds on
  // your production server.
  $md5_iters = 81378;
 
  $hashed_password = md5($clear_text_password); // first hash
  // iterate the hash
  for($ii=0; $ii<$md5_iters; $ii++){
    $hashed_password = md5($hashed_password . $random_salt);
  }
  return $hashed_password;
}
 
// a function to store the password in
function timed_hash($clear_text_password,$random_salt){
  $tic = microtime(true);
  $hashed_password = harden_password($clear_text_password,$random_salt);
  $time_elapsed = microtime(true) - $tic;
 
  return Array(
    "time_elapsed"=>$time_elapsed, // increase md5_iters inside
                                   // harden_password() until about .1
                                   // seconds
    "hashed_password"=>$hashed_password // store this and the
                                        // $random_salt in the
                                        // database
 
               );
}
 
function validate_password(
  $clear_text_password, // gotten from user i.e. from $_POST['password']
  $random_salt, // stored on a per user basis
  $hashed_password // password hash from the database
                           ){
 
  $hardened_password = harden_password($clear_text_password,$random_salt);
  return ($hardened_password == $hashed_password);
}
 
 
$result = timed_hash($clear_text_password,$random_salt);
$time_elapsed = $result['time_elapsed'];
$hardened_password = $result['hashed_password'];
echo "Time Elapsed: $time_elapsed\n";
 
if(validate_password($clear_text_password,$random_salt,$hardened_password)){
  echo "Passwords Match!\n";
}else{
  echo "Passwords Do Not Match\n";
}

Hopefully now you can see how to use the more portable MD5 Correctly and understand that hashing for security is less about the hashing alogrithm but more about the number of CPU cycles to use.

Please rate and comment or hire me for freelance work, thanks!

Share
This entry was posted in beginner-programming. Bookmark the permalink. Post a comment or leave a trackback: Trackback URL.
1 Star2 Stars3 Stars4 Stars5 Stars (No Ratings Yet)
Loading ... Loading ...

Post a Comment

Your email is never published nor shared. Required fields are marked *

*
*

You may use these HTML tags and attributes:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre lang="" line="" escaped="">

If you are going to post code please use:
<pre lang="php" escaped="true"> YOUR_CODE_HERE </pre>

Change the lang to mysql, python, lisp, whatever. This will escape your code.