diff --git a/examples/auth/app.js b/examples/auth/app.js index c799253ae0..188413f9c5 100644 --- a/examples/auth/app.js +++ b/examples/auth/app.js @@ -4,7 +4,7 @@ */ var express = require('../../lib/express') - , crypto = require('crypto'); + , hash = require('./pass').hash; var app = module.exports = express(); @@ -27,24 +27,22 @@ app.locals.use(function(req,res){ if (msg) res.locals.message = '

' + msg + '

'; }) -// Generate a salt for the user to prevent rainbow table attacks -// for better security take a look at the bcrypt c++ addon: -// https://github.com/ncb000gt/node.bcrypt.js +// dummy database + var users = { - tj: { - name: 'tj' - , salt: 'randomly-generated-salt' - , pass: hash('foobar', 'randomly-generated-salt') - } + tj: { name: 'tj' } }; -// Used to generate a hash of the plain-text password + salt -function hash(msg, key) { - return crypto - .createHmac('sha256', key) - .update(msg) - .digest('hex'); -} +// when you create a user, generate a salt +// and hash the password ('foobar' is the pass here) + +hash('foobar', function(err, salt, hash){ + if (err) throw err; + // store the salt & hash in the "db" + users.tj.salt = salt; + users.tj.hash = hash; +}); + // Authenticate using our plain-object database of doom! function authenticate(name, pass, fn) { @@ -55,9 +53,11 @@ function authenticate(name, pass, fn) { // apply the same algorithm to the POSTed password, applying // the hash against the pass / salt, if there is a match we // found the user - if (user.pass == hash(pass, user.salt)) return fn(null, user); - // Otherwise password is invalid - fn(new Error('invalid password')); + hash(pass, user.salt, function(err, hash){ + if (err) return fn(err); + if (hash == user.hash) return fn(null, user); + fn(new Error('invalid password')); + }) } function restrict(req, res, next) { diff --git a/examples/auth/pass.js b/examples/auth/pass.js new file mode 100644 index 0000000000..1ffed2d8e3 --- /dev/null +++ b/examples/auth/pass.js @@ -0,0 +1,44 @@ + +/** + * Module dependencies. + */ + +var crypto = require('crypto'); + +/** + * Bytesize. + */ + +var len = 128; + +/** + * Iterations. ~300ms + */ + +var iterations = 12000; + +/** + * Hashes a password with optional `salt`, otherwise + * generate a salt for `pass` and invoke `fn(err, salt, hash)`. + * + * @param {String} password to hash + * @param {String} optional salt + * @param {Function} callback + * @api public + */ + +exports.hash = function (pwd, salt, fn) { + if (3 == arguments.length) { + crypto.pbkdf2(pwd, salt, iterations, len, fn); + } else { + fn = salt; + crypto.randomBytes(len, function(err, salt){ + if (err) return fn(err); + salt = salt.toString('base64'); + crypto.pbkdf2(pwd, salt, iterations, len, function(err, hash){ + if (err) return fn(err); + fn(null, salt, hash); + }); + }); + } +};