Not all hashing functions are created equally, some are considered more secure than others and yet all of them are more secure than storing plaintext passwords. In this post I’m going to discuss some of the common PHP hashing functions that can be used as an alternative to storing plaintext passwords.
The built-in MD5 function is probably the most common functions to encrypt passwords. Feed the function a string and a 32-bit hash is returned. Unfortunately, it’s not considered secure enough for password hashing as a collision vulnerability exists. When searching for MD5 on Google, the first suggestion was “md5 decrypter” if that’s any indication as to how vulnerable it may be.
I like to consider SHA-1 the big brother to MD5. Out of the gate, the function generates a 40-bit hash, obviously more secure (that would be sarcasm 😉 Actually it’s not more secure as it is affected by the same sort of collision vulnerability as MD5 and is also not recommended for securing passwords. Both MD5 and SHA-1 are great for generating fixed-length values that could be used as unique identifiers so they still have a purpose outside of security.
hash() and crypt()
crypt() essentially do the same thing, they generate a hash from a string based on what you pass in. Aside from the fact that the two functions support a mixed lot of algorithms, the big downside of
hash() is that the only parameter is what type of hash you want to generate (no built in salting). Also, the function is not guaranteed to be present on a server as it can be disabled at compile time. The
crypt() function on the other hand allows you to specify more parameters to generate your hash. Personally, I wouldn’t mess with
crypt() is part of the core of PHP.
crypt() function allows you to generate hashes as Standard DES, Extended DES, MD5, Blowfish, SHA-256 and SHA-512. MD5 still isn’t recommended for security purposes and as per the PHP documentation, Blowfish is the recommended method for encrypting passwords (at the time of this writing). The
crypt() function takes a second parameter (first is the string to hash) for the salt. The salt is a specially formatted string that tells
crypt() which algorithm to use to do the hashing and may contain additional options as well as an actual salt value.
Standard and Extended DES
The Data Encryption Standard, Standard DES uses a salt value of 2 alphanumeric characters. Extended DES is a bit more complicated using a 9 character string starting with an underscore. That is followed y 4 bytes for the iteration count and then 4 bytes of salt. Both of the 4 byte blocks are represented by printable alphanumeric characters and the iteration can contain periods that represent no rounds.
Full disclosure, I’ve never actually used DES before as I remember when distributed.net ended up cracking a DES key back in the day. It took a bit of time but proved it was possible even if it wasn’t practical.
To be able to tell
crypt() to use Blowfish, we have to pass it a more sophisticated salt parameter:
echo crypt('password-to-encrypt', '$2a$07$YourSaltIsA22ChrString$'
To break down the salt value a bit, the first 4 characters are the mode. PHP only supports
$2a$ before PHP 5.3.7 and has added support for
$2y$ in 5.3.7 and newer. The additional modes were introduced to address potential high-bit attacks in the Blowfish implementation in PHP. It’s recommended to use
$2y$ if you are using PHP 5.3.7 or newer.
07 is the “cost” of the function, it represents how many rounds the function will be applied.
07 translates to 2^7 or 128 rounds. This value must be at least
04 and can be as high as
31. The higher the more secure the function will at the sacrifice of speed.
The next part of the string is
YourSaltIsA22ChrString, that’s your provided salt that will be used to generate the hash. If you supply anything over 22 characters it will be ignored and anything less will be padded. You end the salt value with another
$ for good measure.
SHA-256 and SHA-512
Due to the fact that Blowfish has vulnerabilities prior to PHP 5.3.7 you maybe interested in using SHA-256 or SHA-512 instead. Both have a similar salt format as Blowfish (use a prefix of
$5$ for SHA-256 and
$6$ for SHA-512) but also contains an optional
rounds parameter to force multiple hashing. The salt itself is a bit shorter at only 16 characters but unlike Blowfish allows more than just alphanumerics.
echo 'SHA-256 (no rounds): ' . crypt('password-to-encrypt', '$5$YourSaltyStringz$'
echo 'SHA-512 (with rounds): ' . crypt('password-to-encrypt', '$6$rounds=1000$YourSaltyStringz$'
Like Blowfish, the resulting hashes will contain the salt as part of the result hash.
As we can see, not all hashing functions are created equal. Tomorrow I’m going to discuss some additional techniques that can be applied while generating your password hashes regardless of which hashing function you choose to use.