1
\$\begingroup\$

I would like to make a safe way to store hashes of user passwords and verify them later. Here is crypt.js:

'use strict'

const crypto = require('crypto')

module.exports = {

    keyLength: 512,
    hashAlg: 'sha512',
    rounds: 10000,

    randHex(length) {
        return crypto.randomBytes(length / 2).toString('hex')
    },

    hashPassword(plainText) {
        let salt = this.randHex(64)
        let pbkdf2 = crypto.pbkdf2Sync(
            plainText,
            salt,
            this.rounds,
            this.keyLength,
            this.hashAlg
        ).toString('hex')

        return `${this.hashAlg}:${this.keyLength}:${this.rounds}:${salt}:${pbkdf2}`
    },

    verifyPassword(givenPassword, hashedPassword) {
        let splits = hashedPassword.split(':')
        let hashAlg = splits[0]
        let keyLength = parseInt(splits[1])
        let rounds = parseInt(splits[2])
        let salt = splits[3]
        let pbkdf2 = splits[4]

        let testPbkdf2 = crypto.pbkdf2Sync(
            givenPassword,
            salt,
            rounds,
            keyLength,
            hashAlg
        ).toString('hex')

        return testPbkdf2 === pbkdf2
    }

}

I have some questions.

  1. It seems that pbkdf2 might be the best that Node's Crypt offers. Is this true? If so, is there a good reason to avoid pbkdf2Sync and use pbkdf2 instead?

  2. I am adding in a lot of info such as the salt, rounds, hashing algorithm, etc. so that I can change this stuff and still be able to verify passwords hashed the old way. The database will have someone's password as sha512:512:10000:d15...d5k:a6a...1b9e Is it okay to do this?

  3. What other dangers are there with crypt.js?

\$\endgroup\$
2
\$\begingroup\$

Glad to see you are using an adaptive encryption approach in PBKDF2.

You might consider Argon2 library as a more specific use password encryption tool as Argon2 is now the OWASP-recommended password encryption algorithm for new implementations.


Even though you are using an adaptive encryption approach, you are not really doing anything to facilitate the "adaptive" part. Let's say this code has been working in an application for a year or two and you decide at some point that you want to increase your number of iterations from 10K to 20K, or change the underlying encryption algorithm, or any of the other settings. What happens when you verify a password against a hash made using old settings?

Your code, as shown, could still handle the verification part in that the hash contains all the needed information for re-hashing the password being verified. This is a very common and reasonable way to be able to perform password verification. However, a password verification event is the only opportunity you have to update the stored hash to a new hash based on new default settings. As such, you may consider being able to trigger an update of the password hash to a new hash if the current hash settings are different than the ones for the stored hash that is being used for verification.

Of course this logic doesn't need to exist in this specific module (it could exist outside this module higher up in application call stack), but it might mean that the verifyPassword() function might need to be aware of current settings and trigger a callback to update password hash, or change the return such that caller gets indication that there is need to update hash upon successful verification of password in old hash format.


Based on above comments, you might consider deriving your default password encryption settings from configuration rather than having them hard-coded in your module.


You may consider adding the ability to log out hash creation timings so as to allow you to more easily tune the work factor value to meet appropriate use case for your application (i.e. say you wanted the hashing process to take 1 second, you could tune the rounds value to achieve that approximate completion time for you application and environment). This is another reason why you should probably store hash settings in config, to make it easier to tune the encryption for your application without having to actually change your code.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.