Please, Stop Hashing Passwords Yourself
Any web developer writing PHP applications is eventually going to have to store user passwords. Most developers have at least learned that storing a password in plain-text isn’t secure, but for many of us, we still use insecure methods.
The problem is, that if our web application is ever hacked, insecure password hashing algorithms will make it simple, easy and quick for attackers to break our passwords.
The problem in two code samples
For those developers who understand they can’t simply store the password in plain text, they typically employ one of two mechanisms. Both are insecure.
The first mechanism is using some type of insecure hashing algorithm, like MD5 or SHA1. These “fingerprint hashes” produce the same hash for a given string of characters, making them ideal for identifying files and other items, but are not cryptographically strong enough for hashing passwords. Even if you add a salt, your passwords will be inherently insecure
especially since these algorithms require that the salt be stored alongside the password for two reasons: first, you are likely to screw up the randomness of the salt (weakening the encryption). Second, MD5 and SHA1 are meant to be fast hash algorithms, meaning that a determined attacker can brute force their way through your passwords with ease.
<?php $hashed = insecure_hashing_function($salt . $password); # This is BAD
Another potential failure of security for developers is somehow encrypting passwords with a key, so they can be decrypted for various purposes. I came across the following code sample in a project I was auditing:
The ONLY surefire way to hash passwords in PHP
So, if “traditional” methods aren’t enough, what can a developer who cares about security do? The answer lies in a feature introduced as of PHP 5.5: a native password hashing API for PHP.
This library contains two functions that will make it easier for you to hash passwords: password_hash() and password_verify(). These functions essentially wrap built-in PHP behaviors for cryptography, but build in sane defaults that will serve even the most inexperienced developer well.
<?php $password = 'SuperS3cr3+P@ssw0rd'; $hash = password_hash($password, PASSWORD_DEFAULT); # $2y$10$PwbhVaqzuUSl5wSdi.iCK.RBz5pQp/a.6lRdSRttY8s2qs7dn5hTq $verified = password_verify($password, $hash); # true
Astute developers who run the above code will recognize that the hash I give isn’t repeated by their own password_hash function. That’s by design: each password hash should be unique, even for the same password between different users! Also, the second argument lets developers pick the encryption algorithm they want to use; PHP 5.5 defaults to bcrypt, which is usually sufficient.
But I don’t have access to PHP 5.5!
Ah, that is a problem, isn’t it? PHP 5.5’s adoption rate is fairly low right now. But you’re in luck: Anthony Ferrara has backported the native password library for earlier versions of PHP. You can check out his work on GitHub. The library requires PHP 5.3.7 and later, which is well-distributed.
The bottom line is that there’s no reason for you to be hashing passwords yourself. You should be relying on a well-established methodology that will help keep your users secure in the event of a data breach.