salt'n'pepper - or how to store passwords securely

Just yesterday I received an email from kickstarter telling me that their database servers were hacked and that someone was able to access all my account data:

Accessed information included usernames, email addresses, mailing addresses, phone numbers, and encrypted passwords. Actual passwords were not revealed, […]

Good to know, that they didn’t store the password in plain text but encrypted the most sensitive data.

You see, in today’s world it is very important to make sure that an attacker, after aquiring your user data, isn’t able to just know you password and:

1) log into your account
2) more importantly – use the password for other services with which he/she could really “harm” you

[…] however it is possible for a malicious person with enough computing power to guess and crack an encrypted password, particularly a weak or obvious one.

That’s true – even when using encryption. So, we as developers have to make sure that this process of “computing” or “guessing” takes as long as possible (and has to be repeated for every password).

I can remember my first attemps at server-side user-management with PHP 13-14 years ago. I just stored the user’s data like this:

----------------------
| user | password    |
----------------------
| phil | topsecret   |
| aron | notsosecret |
----------------------

The user sent the data via POST to the server where I would compare the password he/she just entered with the password stored inside my database like this:

// don't do this
if ($_POST["password"] === $row["password"]) {
  // grant access ...
}

If the user ever forgot his/her password, I could just get it from the DB and resend it via email :( – a really bad idea! But back in the days I didn’t know better.

Rule #1
Never save passwords in plain text

Use a hash function to store the password securely. Hash functions are one-way algorithms that turn your plain text password into a totally unrecognizable number and letter combination. Plus they produce results that differ completely even if the input is almost the same.

The following snippets are JavaScript because our last project was a node.js project – but these functions are available in other programming languages as well.

View code on gist.github.com

You get the idea …

But this is not enough. If we save the once hashed password into the DB, hackers can use methods like “dictionary”–, “brute force”-attacks, lookup– or rainbow-tables to “guess” your password. It’ll take some time, but they will get there – and even faster if the original password only contains letters from a-z.

Rule #2
Salt your password before saving it

Hashing function always produce the same result if the input is the same. You can run the code above as many times as you like, the result won’t change.
Two users with the same password would get the same hash, which makes it easier for an attacker because he/she can use lookup- or rainbow-tables to “decrypt” your password.

Appending a random string (a salt) to the password makes this impossible.

I don’t know if they still do, but in a previous version wordpress used the hashed SITE_URL as a salt for every password – not good. You can do better than that.

Create a new salt everytime you store a password in the database – never reuse an old one. And don’t make the salt to short. It gets harder to hack the longer the salt is.

var crypto = require("crypto");
var salt = crypto.randomBytes(64);

If you don’t use the same salt for every password, you have to store the salt in the database as well. Either in a seperate column or add it to the password as “metadata”.

------------------------------------------
| user | password                        |
------------------------------------------
| phil | <hashed password + salt>:<salt> |
| aron | <hashed password + salt>:<salt> |
------------------------------------------

Salting the passwords prevents hackers from using lookup-tables to gain access to your passwords quickly but with up-to-date hardware they can use “brute force” to generate billions of hashes a second and compare them with the hashes you stored in the database.

Rule #3
Use “slow” hash functions

This may sound strange but another method to make sure it’ll take attackers more time to hack the passwords is to use an encryption algorithm that takes it time to produce a result. This reduces the number of hashes a hacker can generate to a fraction of the number above.

PBKDF2 is a good choice for this.

Storing the encrypted password

View code on gist.github.com

Retrieving the encrypted password

View code on gist.github.com

Hope this summary helps and makes your next project a bit more secure (than it already is).

Comments