Encrypting Passwords in PHP

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.

md5()

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.

sha1()

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()

Both hash() and 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 hash() since crypt() is part of the core of PHP.

The 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.

Blowfish

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 $2x$ and $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.

The 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.

Conclusion

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.

Josh Sherman - The Man, The Myth, The Avatar

About Josh

Husband. Father. Pug dad. Musician. Founder of Holiday API, Head of Engineering and Emoji Specialist at Mailshake, and author of the best damn Lorem Ipsum Library for PHP.


If you found this article helpful, please consider buying me a coffee.