9

I'm looking for a straightforward, secure, solution for storing a user's password using Node. I'm a cryptography novice, but have been trying to piece a solution together from researching online. I'm looking for validation that what I came up with is a solid solution for a web app with basic (not a bank, hospital, etc) security needs. Here it is:

var crypto = require('crypto');
var SALT_LENGTH = 64;
var KEY_LENGTH = 64;
var ITERATIONS = 1000;

function createHashedPassword(plainTextPassword, cb) {
    crypto.randomBytes(SALT_LENGTH, function (err, salt) {
        console.time('password-hash');
        crypto.pbkdf2(plainTextPassword, salt, ITERATIONS, KEY_LENGTH, function (err, derivedKey) {
            console.timeEnd('password-hash');
            return cb(null, {derivedKey: derivedKey, salt: salt, iterations: ITERATIONS});
        });
    });
};

...and here are the choices I made that brought me to this point:

What hashing algorithm to use?

Based on this widely referenced article, it looks like the leading contenders are PBKDF2, bcrypt, and scrypt. I chose PBKDF2 because it has built in support in Node.

What salt size to use?

This stack overflow answer seemed to be the most straightforward answer I could find. I'm still not very clear on why 64 bytes is the right salt size though. When I google around, I get other stack exchange answers like this, but I'm not sure it applies to the Node algorithm? Totally confused here, an explanation aimed at a novice using this Node function would be awesome.

What key length to use?

Once again, I largely based my choice off the same answer as above, but I'm just as foggy on the basics of 'why'. The answer says that 'it's a waste to generate keys smaller than your input, so use at least 64 bytes'. Huh? Once again, a practical explanation would be helpful.

How many iterations to use?

For this question, I based my choice off this stack exchange answer. I don't understand much of it, but I did get that the algorithm is supposed to take approximately 8ms. So, as you can see I put timers on the function, and I adjusted my iterations to get it in that ballpark on my machine.

Thanks!

Community
  • 1
  • 1
markdb314
  • 4,636
  • 5
  • 23
  • 24
  • When it comes to security, it's usually better to stick to the well known and well tested libraries. Passport.JS comes to mind and it has plugins you can use to handle hashing. – Pier-Luc Gendreau Oct 23 '14 at 00:32
  • Thanks @Pier-LucGendreau. I am using PassportJS, but it doesn't include the code for hashing and storing passwords as part of its library, or it's sub-modules. If I'm missing something, please let me know. – markdb314 Oct 23 '14 at 20:21
  • You can use https://github.com/saintedlama/passport-local-mongoose – Pier-Luc Gendreau Oct 23 '14 at 20:49
  • Ok, I took a look at that... but, I'm seeing more seemingly arbitrary choices for the constants I mentioned above. They choose a a default salt length of 32 bytes, key length of 512 bytes, and iterations of 25000. In my comment for an answer re: the 'credential' node package below, the creator says "The salt should be the same size as the hash. No shorter, and no longer". One of these implementations isn't right! This is demonstrating what I've seen over the course of researching this... every implementation I find uses different values for these constants, with no explanation why. – markdb314 Oct 23 '14 at 21:47
  • Above all, remember that no solution is bullet proof. Today's best solution for brute forcing is slow hashes! 20 years ago you could easily rely on DES or RSA, today none of these are safe. It's layers on layers with third party verification and even then you are just hand waving.... – winmutt Oct 24 '14 at 02:28

2 Answers2

6

The NPM package credential handles all of these

You can see the author's write up on it in the book Programming Javascript Applications

Alex Hill
  • 693
  • 1
  • 5
  • 13
  • I looked at those links, and the 'credential' package seems to be doing the same type of thing I have above. In his source, he uses a salt length and key length of 66 bytes, and a default of 1000 iterations. In the readme for the package, he says " The salt should be the same size as the hash. No shorter, and no longer". This is all good info, but I'd like to see at least a basic explanation of 'why'? – markdb314 Oct 23 '14 at 20:18
  • @markdb314 The 'why' would probably be better explained by http://security.stackexchange.com/ – Pier-Luc Gendreau Oct 23 '14 at 20:50
  • Yeah, I may post there... but the answers usually go into more detail than I can follow. I'm just looking for someone to tell me with confidence what constants to use, and a simple 'why'. – markdb314 Oct 23 '14 at 21:52
3

I would strongly recommend using BCrypt. There are lots of advantages to the algorithm, and most implementations handle all of these questions for you.

As described in this answer:

Bcrypt has the best kind of repute that can be achieved for a cryptographic algorithm: it has been around for quite some time, used quite widely, "attracted attention", and yet remains unbroken to date.

I've written up a detailed article about how to implement BCrypt in node/express as well as other frameworks here: http://davismj.me/blog/bcrypt

Community
  • 1
  • 1
Matthew James Davis
  • 11,744
  • 6
  • 56
  • 86