Strong Password Hashing with Node.js Standard Library

Cracking your users' hashed and salted password is pretty damn easy these days. To make it a bit harder for the bad guys, you should use something like PBDFK2, which hashes the password thousands of times before giving you back the result. This is called key-stretching and it helps simply by making your passwords more computationally expensive to crack.

Here's a neat little function you can use to hash (and then verify) your passwords in Node.js, using its crypto module.

crypto = require 'crypto'

hasher = (opts, callback) ->
  # Generate a random 8-character base64 password if none provided
  if not opts.plaintext
    return crypto.randomBytes 6, (err, buf) ->
      callback err if err
      opts.plaintext = buf.toString 'base64'
      hasher opts, callback
  # Generate random 512-bit salt if no salt provided
  if not opts.salt
    return crypto.randomBytes 64, (err, buf) ->
      callback err if err
      opts.salt = buf
      hasher opts, callback
  # Node.js PBKDF2 forces sha1
  opts.hash = 'sha1'
  opts.iterations = opts.iterations ? 10000
  crypto.pbkdf2 opts.plaintext, opts.salt, opts.iterations, 64, (err, key) ->
    callback err if err
    opts.key = new Buffer(key)
    callback null, opts

opts is an object with a number of optional components:

Your callback should accept an object similar to the above, but with an additional component: key - the resulting 512-bit hash in the form of a Buffer.

Hashing a password

hasher {plaintext: 'secret'}, (err, result) ->
  # Save as hex strings
  user.salt = result.salt.toString 'hex'
  user.key = result.key.toString 'hex'
  user.save()

Verifying a password

# Hex string to Binary
salt = new Buffer user.salt, 'hex'
hasher {plaintext: 'secret', salt: salt}, (err, result) ->
  if user.key == result.key.toString 'hex'
    console.log 'Success!'

Generating a password

hasher {}, (err, result) ->
  # Save as hex strings
  user.salt = result.salt.toString 'hex'
  user.key = result.key.toString 'hex'
  user.save ->
    postmark.send
      From: "you@example.com"
      To: user.email
      Subject: "Thank you for signing up with Example.com"
      TextBody: "Your temporary password is #{result.plaintext}"

Let's end on a troubling note, courtesy of scrypt and this StackOverflow answer.

Prices for password cracking

Hash, rinse, repeat!