Library of the Week: Password Hashing 101

This library of the week is not a single library, but rather a few different libraries used for password hashing. But first a little background.

Everybody knows that storing plain text passwords is a bad idea. Storing plain text hashes is not much better; with lookup tables attackers can still crack passwords relatively quickly. For better security, a random string unique to each user—known as "salt"—is hashed together with the password. But even salted passwords are not safe from brute-force attacks (e.g. using the GPU, which can be used to compute many hashes in parallel). For this reason, another technique called key-stretching was developed. It significantly increases the time needed to compute each hash, slowing down attackers considerably.

There are many great articles covering this topic better than I can do here (such as Salted Password Hashing - Doing it Right). In any case, a deep understanding of the techical underpinnings isn't necessary to implement hashing properly. We just need to follow best practices such as those outlined in the aforementioned article. Two things are required: a cryptographically strong random salt (not Math.random()!) and a key-stretching algorithm.

PBKDF2

One suitable algorithm is PBKDF2. In Node.js, everything you need is available in the standard crypto package:

var crypto = require('crypto'),
	salt = crypto.randomBytes(20).toString('base64'),
    hash = crypto.pbkdf2Sync('P4$$W0rd', salt, 4096, 20, 'sha256').toString('base64');

Browser implementation is more complicated (fortunately we usually don't need password hashing on the client side too often). Modern browsers support the Web Cryptography API, so getting a random salt is straightforward:

var crypto = window.crypto || window.msCrypto,
	arr = new Uint8Array(20);
   
crypto.getRandomValues(arr);
var salt = Array.prototype.map.call(arr, function(n) {
		return n.toString(16);
	}).join('');

In older browsers, the only option is to use a tricky approach like collecting entropy from mouse movements (see Fortuna PRNG).

Finally, we need to generate the key itself. Cryptography API support for this is unfortunately still weak. So once again we must turn to an external library such as this browser implementation of PBKDF2.

bcrypt

Another suitable algorithm is bcrypt. There is no built-in function for this in the crypto module, but we can use the bcrypt package. It contains convenient methods for salt generation and verifying hashes. It can be used in the browser (as long as it supports the Web Cryptography API), which means that overall it can be easier to use then PBKDF2.

var bcrypt = require('bcrypt'),
	salt = bcrypt.genSaltSync(10),
	hash = bcrypt.hashSync('P4$$W0rd', salt);
Roman Krejčík

Roman Krejčík