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!
The Speed of Various Existential PHP Functions
You want to right a pretty existential function in PHP so you can do things like the following without throwing warnings (warnings that write to disc, or the screen, slow things down and burn platters).
If
$ar[1][2][3]is not set then a warning is thrown.Examples of Places You Should Use Existential Calls
Here are some timed tests of existential functions. The most common is
the ternary
isset.Ternary Existential Calls (ugly)
But i find that syntax ugly, i would like to do something more cogent, with out all the extra
?and:in theif‘s predicate. I have created a function isgd stands for “Is Set Get” This would be nicer, but it comes at a speed cost.An Existential Function Call
Here are a list of 5 solutions for the existential crisis, I have a speed summary as well as an explanation/guideline below.
An Existential Speed Tests
Summary
: ?is the fastest, but is bulky.isgis slower than@with little benefit. Ternary is the fastest and allows default, but also pretty ugly inside of code. Ugly code takes a long time to debug so only use Ternary when all that maters is super-fast code.array_multi_key_existsandisgare both slower than@but because there is not default there is little benefit.eval_iffis just bad all around.So if you don’t need a default and NULL is OK, then use @. If you do need a default and you don’t need the particular check to be very fast (like its only called a handful of times) then use
isgd. If must be really fast so being ugly doesn’t matter use the Ternary.