GillesC GillesC - 5 months ago 17
Node.js Question

How to validate Symfony2 sha512 passwords using nodejs

I need to be able to validate in

node
some password which were generated and stored using
Symfony2
with
sha512
encoding.

I can retrieve the
hash
and the
salt
just fine but when using
crypto
I cannot manage to generate a hash using the salt which matches the one stored in the database.

Symfony security.yml

security:
encoders:
"FOS\UserBundle\Model\UserInterface": sha512


Hash stored in DB

6zxwRZc4EPXKxQes9avs0ZyCRFkC4dtpXrT983ML8VLvv9WhRnAi282bwuFuj3LHPQBGmqD1BfCLDUXGdHIjZQ==


Salt stored in DB

qu7rjvaietws8kg4cgsggksookwsws8


As there is a salt on the node side I'm using
crypto.pbkdf2Sync
, there is no iterations set in the configuration and it seems like the Symfony default is 1000. The default length however is 40 yet the stored hash length is 128 so using 128 as length (tried 40 without success).

I have also tried 5000 iterations as used by Symfony 2
MessageDigestPasswordEncoder
and merging the raw password with the salt as symfony does
raw_password{salt}
without success.

I can also get a base64 long enough if I use
pbkdf2Sync
to generate a key of length
40
(as per symfony default) and then using that to update a hash generate with
crypto.createHash


var hash = user.password;
var salt = user.salt;

console.log(hash);

console.log(crypto.pbkdf2Sync("password", salt, 1000, 128 >> 1, "sha512").toString("base64"));


The size of the hash generated and format matches the one stored in the database but they do not match in values which is my issue. I also tried multiple iteration values dynamically generated without any luck.

Output

6zxwRZc4EPXKxQes9avs0ZyCRFkC4dtpXrT983ML8VLvv9WhRnAi282bwuFuj3LHPQBGmqD1BfCLDUXGdHIjZQ==
5QPz3zXwhak/bTD2S9IFEEFmiJ8q/fqIlSF6cWin7dqmh92EFNXjw/FLtQw7NX3LVehwcXKjfypo2EhJxiLouQ==
w9+8xjklvGq9CuiqY8tEoxEetLV9lbhLJ/KaFQEooUFJrGT9/EdsVd/sSRJ+DXjsH4RQeaqsmftmuzLPgVv5MA==


How can I generate the hash so it matches the way used by Symfony 2?

Answer

Symfony does 5000 iterations with sha512.

  1. They concatenate raw password with user's salt to generate a new salt
  2. They generate a starting hash with above data.
  3. On each iteration previous hash its updated with the current hash + new salt generated at step 1.

In node, on each iteration you should digest previous hash as binary and in the end digest as base64, to mimic what Symfony does.

And an example:

var crypto = require('crypto');

var encodePassword = function (raw, salt) {
    var salted = raw + '{'+salt+'}',
        hash = crypto.createHash('sha512').update(salted, 'utf-8');

    for (var i = 1; i < 5000 ; i++) {
        hash = crypto.createHash('sha512').update(hash.digest('binary')+salted);
    }

    return hash.digest('base64');
};

console.log("Password: "+ encodePassword("secret", "h2zaays1cx2og00c6ow2gc0k4skg41g"));