Lets create a repository of various techniques to securely hash and store passwords. Something which is overlooked by many people.
One of the most important factors, I believe, when handling passwords is no restrictions. Don’t force a certain character sets to be used or force complexity, do not limit size (min size is okay), etc. In other words, let the user enter whatever it is they please. If I can enter a long complicated SQL string into the password field and not have any issues, you are doing it right…maybe?
When I get a password I barely touch it, I throw it into a hashing function as quickly as possible. The moment it goes into that hashing function it is completely sanitized and devoid of any nasties that may harm the system. It won’t matter what the user enters or how big it might be, the hash digest it does to a set pattern. Thus you don’t have to filter, validate or escape the password.
Another technique I use is salting and peppering (might not be official terms), I have a user specific salt created for each user, then for every site there is a key for peppering. If an attacker manages to break into the database and get all the hashed passwords and salts they can still create a rainbow table it will just take a little longer (GPUs speed this up greatly). However, with the additional pepper, not only will they need the database but also the source code for the site. Not improbable but much more difficult to attain both.
For the sake of example, below I will generate both the site key and the user key dynamically each time the script is ran. The examples are also using PHP, if you have GOOD examples in another languages you would like to share please post them.
$example_sitekey = mcrypt_create_iv( 4096, MCRYPT_DEV_RANDOM ); // Static in Production
$example_userkey = mcrypt_create_iv( 4096, MCRYPT_DEV_RANDOM ); // Generated during registration
You may be asking yourself, “Why am I using mcrypt? Is that not for encryption?” Indeed it is, mcrypt_create_iv is for creating initialization vectors for various ciphers in mcrypt. However, it can be exploited and used as a [URL=“https://en.wikipedia.org/wiki/Cryptographically_secure_pseudorandom_number_generator”]Cryptographically Secure PseudoRandom Number Generator (CSPRNG) both on Linux/Unix and Window systems since version 5.3. (Various versions of Linux/Unix, however, can have different implementations and results affecting security, be aware of that.)
Moving onto the next line.
$userid = '5ccd875c-e10f-11e1-8b47-d818ed2c1c51'; // Gen UUID at registration
$username = 'Logic';
$password = 'MyNotSoSecurePassword!!11!!eleven';
$password = "auth://{$username}:{$password}@auth.example.com/{$userid}/";
This just happens to be some of the data I have for users, every user as a unique name and UUID associated with their accounts. Now the password…quite odd putting it into a URI format…why? Well simply to make it more unique and to pad its length. In case a user entered a short password. The longer the password the better it can be digested. Plus I like the [URL=“https://en.wikipedia.org/wiki/URI”]URI format, so sue me.
function hmac ( $data, $key, $raw = true )
{
// sha512 blocksize = 1024
$blockSize = 1024;
$len = strlen( $key );
if ( $len < $blockSize ) {
$key = str_pad( $key, $blockSize, "\\0" );
$len = $blockSize;
}
$opad = str_repeat( chr( 0x5C ), $len );
$ipad = str_repeat( chr( 0x36 ), $len );
for ( $i = 0; $i < $len; $i++ ) {
$opad[ $i ] = $opad[ $i ] ^ $key[ $i ];
$ipad[ $i ] = $ipad[ $i ] ^ $key[ $i ];
}
return hash( 'sha512', $opad .
hash( 'sha512', $ipad . $data, true ), $raw );
}
Now I know PHP already has hash_hmac available. However, I have one problem with the default spec implementation of [URL=“https://en.wikipedia.org/wiki/HMAC”]HMAC and that is the key truncating for long keys (which I use). I wanted to retain the length of the key as much as possible. As defined: “K be a secret key padded to the right with extra zeros to the input block size of the hash function, or the hash of the original key if it’s longer than that block size” That is pretty much the only change.
function stretch ( $data, $key, $iteration = 64 )
{
$keyLength = ceil( $iteration / 2 );
$output = '';
for ( $block = 1; $block <= $keyLength; $block++ ) {
$last = $xor = hmac( $data, $key . pack( 'N', $block ) );
for ( $i = 1; $i < $iteration; $i++ )
$xor ^= ( $last = hmac( $data, $last ) );
$output .= $xor;
}
return hmac( $output, $key );
}
Key stretching based on [URL=“https://en.wikipedia.org/wiki/PBKDF2”]PBKDF2 (not strict), fairly common these days, increasing the computation time to hash, slowing down brute force rainbow tables. You don’t need a high iteration to get good results…too high and you might kill your server. Having stronger keys when hashing is far better, but we can still use key stretching to make a weak key stronger by using it.
Alright now, that is all the major code, simplified greatly of course. Here is how it might all go together: ($db might be how it could be stored)
$password = stretch( $password, $example_userkey );
// Prepare for storing:
$output = hmac( $password, $example_userkey );
$output = hmac( $output, $example_sitekey );
$db = base64_encode( $output . "\\0\\0\\1\\0\\0" . $example_userkey )
// nul-nul-soh-nul-nul patten seperates the salt
Any questions or improvements?