11
$\begingroup$

I am new to crypto and authentication so have been doing reading around this and thought that I had a pretty good understanding of how this works. However when implementing my hashing using bcrypt.js I am once again confused.

I thought that the steps were:

Store Credentials:

  1. Generate random salt
  2. Append salt to password
  3. Generate hash from concatenated string
  4. Store the username, hash and salt

Verify User:

  1. Find user record using username
  2. append stored salt to entered password
  3. hash concatenated string
  4. compare generated hash with stored hash

However reading the bcrypt.js readme I can generate with:

var salt = bcrypt.genSaltSync(10);
var hash = bcrypt.hashSync("B4c0/\/", salt);

then compare with:

bcrypt.compareSync("enteredPassword", storedHash)

So it seems that there is no need to store the salt at all.

Also looking at the generated hashes:

[
    {
        "username": "UserOne",
        "hash": "$2a$10$M6qZCSbwhXipdmMy7kQ4V.obtALLSAjZsMYD/oGDAo0i/fcSGrmn2",
        "salt": "$2a$10$M6qZCSbwhXipdmMy7kQ4V."
    },
    {
        "username": "UserTwo",
        "hash": "$2a$10$Yb04C6pVgKVdNjHRB42vKOkr5Wf4QVG8gXyXVqZWnzCs6/MFnIC9G",
        "salt": "$2a$10$Yb04C6pVgKVdNjHRB42vKO"
    }
]

The salt seems to be appended to the hash. Not appended to the password before hashing. This seems pretty pointless to me as any attacker could just strip the salt from the hashes to get a table of not salted hashes.

As I said I am really new to this stuff and getting my head around it is a little difficult. Any simple explanations much appreciated.

$\endgroup$
45
$\begingroup$

The hash returned by bcrypt.hashSync is more than the hash itself, it contains all parameters needed by bcrypt. You do not need to store anything else yourself, this information is everything bcrypt needs to hash and compare an incoming password.

The actual hash was computed by combining the password and salt, so no worries there. The structured data is there merely to make your life easier and only need a single object to store.

In your first example, the output is:

$2a$10$M6qZCSbwhXipdmMy7kQ4V.obtALLSAjZsMYD/oGDAo0i/fcSGrmn2

This breaks down into:

  • version information: 2a
  • the number of rounds to perform: 10
  • the salt: M6qZCSbwhXipdmMy7kQ4V.
  • the actual hash: obtALLSAjZsMYD/oGDAo0i/fcSGrmn2

You can see the details in the code

| improve this answer | |
$\endgroup$
  • $\begingroup$ very clear explanation. Many Thanks. $\endgroup$ – Roaders May 17 at 10:29
  • 2
    $\begingroup$ i.stack.imgur.com/m8opZ.png $\endgroup$ – online Thomas May 17 at 21:21
  • $\begingroup$ Also important to note: salt is there to protect from pre-computed hashes (rainbow tables), and to make all the geniuses with "password" as password to have different hashes (given RNG gave them different salts). Salt doesn't need to be more secret than hashes. $\endgroup$ – Nyos May 19 at 12:55
  • $\begingroup$ Where did the first uppercase M (8th character) go? $\endgroup$ – Pedro A May 19 at 23:33
  • $\begingroup$ @PedroA copy/paste error on my part. Fixed. $\endgroup$ – Marc May 20 at 4:51

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.