Let's Talk About Passwords

With the increase in hacking attacks on corporations we need to discuss password security. Basically you should avoid all sites that send you an email after you register that includes you password, or sites that send you a temporary password via email when you click the "forgot my password" link. This what is referred to as a plaintext password and it's a sure sign that the site is not properly handling your sensitive information.

This blatent lack of security is primarily due to two things: Ignorance (see my post "Validating, Sanitizing, and You) and a flawed logic called "security through obscurity." It essentially states that if you don't know how something is built then it will be too difficult to find and exploit its vunerabilities.


Cryptographic Hashes

I know, you see the word Cryptographic and start to doze off already but it's important to understand. Using a crytographic hash is an important first step if you want to store passwords securely. How it works is the password is algorithmically turned into a indecipherable series of characters. Let's use MD5 strictly for example simplicity.

$pwrd = "password";
$hash = md5($pwrd); // Value: 5f4dcc3b5aa765d61d8327deb882cf99

The way you verify a password is you take the password, hash it again, and compare the hashes. This seems secure right? Well in theory it is. The issue comes when the innerworkings of the algorithm are found to be flawed, as is the case with MD5 and the collision issue noted in 1996. This means two inputs can have the same hash output. This makes it easier for brute force attacks which makes it easier to fill it's rainbow table. Well how do we prevent this?


Salt

Salt is when you add a random string onto the password and hash it together. This prevents any previously known rainbow tables from working. This also turns your hash into a one way hash because the password can not be computed without knowledge of both the salt and the password.

$pwrd = "password";
$salt = "jonsnow";
$hash = md5($salt . $pwrd); // Value: ab6b5f9f814ccee8a520f97b59f0f0df

So we're secure right? Not quite. This is what's known as a site-wide salt, and it's generally frowned upon because all it takes is someone to know your salt code and you're back to square one. Salts should be randomly generated to prevent this.


Putting it together

First we need to use another crytographic hash because as we've discussed MD5 is broken. The most secure one right now is bcrypt which uses the Eksblowfish algorithm. Luckily for us as of PHP 5.5 we can use the password_hash() function.

Hashing and Salting

$username = 'user';
$password = 'password';
$options = array('cost' => 11);
$hashed = password_hash($password, PASSWORD_BCRYPT, $options);

$params = array(':username' => $username, ':hash' => $hashed);

//Insert hashed and salted password into DB. Connection link code omitted.
$query = $conn->prepare('INSERT INTO users (username,hash) VALUES (:username,:hash)');

$query->execute($params);

In this example you will noticed a cost. The cost "denotes the algorithmic cost that should be used." (PHP.net) The higher the cost the more taxing it is on your hardware. 10 is the default cost, but PHP.net recommends increasing it dependant on your hardware. Also if no salt is defined, and it's typically best not to unless you know what you're doing, then a salt will be randomly generated for you.

Verifying the Password

You'll need a way to check the password entered against the stored hash. PHP also has a simple function for this called password_verify().

$username = 'user';
$password = 'password';

$params = array(':username' => $username);

//Fetch hashed and salted password from DB. Connection link code omitted.
$query = $conn->prepare('SELECT hash FROM users WHERE username = :username LIMIT 1');

$query->execute($params);
$hashed = $query->fetch(PDO::FETCH_OBJ);

if (password_verify($password, $hashed)) { echo 'Password is valid!'; } else { echo 'Invalid password.'; }

Rehashing

This is necessary to keep passwords up to date in case you want to roll out security updates, like increasing or decreasing the cost without forcing users to reset their passwords.

$options = array('cost' => 12);

if (password_verify($password, $hashed)) { if (password_needs_rehash($hashed, PASSWORD_BCRYPT, $options)) { $query = $conn->prepare('UPDATE users SET hash = :hashed WHERE username = :username"'); } echo 'Password is valid!'; } else { echo 'Invalid password.'; }

As you can see this is incredibly easy. This is a good baseline for developers. This should not be the limit by any means though. Some form of two factor authentication should be included. Ideally this should use something you know the user has, like a phone. To do this you can hook into the Google Authenticator app. I do not recommending asking for phone numbers to send SMS pins as it can be seen as overly intrusive, but sending a PIN through email is acceptable albeit naturally less secure.


How Users Can Help Themselves

Users should never login unless the URL is valid and it's using HTTPS with a valid SSL certificate. This I will concede is a burden that most users won't carry, however the consequences are phishing scams and man-in-the-middle attacks. Users should care though, they should have a burden, they should be invested in their own data. Users should also use two factor authentication, as this will help verify it is indeed them due to needing something the user has in order to authenticate.

If you find a site that is using plaintext, please submit them to http://plaintextoffenders.com/. This site compiles all offending sites that it and its users can find for the sake of transparency.


Conclusion

We need to find a way to both urge security standards on developers or find a way to make it transparent to the user before they register that their data is secure even if they can't or won't disclose what methods are used to secure it. Most users assume their data is secure and sites feed on that assumption because it means less work for them. Many others say demolish passwords altogether and stick with something like OpenID Connect. I tend to disagree though. We already have a huge issue with companies not properly storing information correctly, so I think it's a bit near-sighted to advocate for and rely wholly on one company to handle potentially millions of users' data.