Bcrypt and Randomly Generated Salts

bcrypt and randomly generated salts

Have a good look at the values you're dealing with. The random salt generated will be, say:

abcdefg...

What is fed into crypt looks like this:

crypt($password, '$2y$10$abcdefg...')
| | |
| | +- the salt
| +- the cost parameter
+- the algorithm type

The result looks like:

$2y$10$abcdefg...123456789...
| | | |
| | | +- the password hash
| | +- the salt
| +- the cost parameter
+- the algorithm type

In other words, the first part of the resulting hash is the same as the original input into the crypt function; it contains the algorithm type and parameters, the random salt and the hash result.

Input:  $password + $2y$10$abcdefg...
Output: $2y$10$abcdefg...123456789...
^^^^^^^^^^^^^^^^^
first part identical

When you confirm a password, you need the same, original salt again. Only with the same salt will the same password hash to the same hash. And it's still there in the hash, in a format that can be passed to crypt as is to repeat the same operation as when the hash was generated. That's why you need to feed both the password and hash into the validation function:

crypt($passwordToCheck, '$2y$10$abcdefg...123456789...')

crypt takes the first defined number of characters, up to and including abcdefg... and throws the rest away (that's why the salt needs to be a fixed number of characters). Therefore it equals the same operation as before:

crypt($passwordToCheck, '$2y$10$abcdefg...')

And will generate the same hash, if and only if $passwordToCheck is the same.

How can bcrypt have built-in salts?

This is bcrypt:

Generate a random salt. A "cost" factor has been pre-configured. Collect a password.

Derive an encryption key from the password using the salt and cost factor. Use it to encrypt a well-known string. Store the cost, salt, and cipher text. Because these three elements have a known length, it's easy to concatenate them and store them in a single field, yet be able to split them apart later.

When someone tries to authenticate, retrieve the stored cost and salt. Derive a key from the input password, cost and salt. Encrypt the same well-known string. If the generated cipher text matches the stored cipher text, the password is a match.

Bcrypt operates in a very similar manner to more traditional schemes based on algorithms like PBKDF2. The main difference is its use of a derived key to encrypt known plain text; other schemes (reasonably) assume the key derivation function is irreversible, and store the derived key directly.


Stored in the database, a bcrypt "hash" might look something like this:

$2a$10$vI8aWBnW3fID.ZQ4/zo1G.q1lRps.9cGLcZEiGDMVr5yUP1KUOYTa

This is actually three fields, delimited by "$":

  • 2a identifies the bcrypt algorithm version that was used.
  • 10 is the cost factor; 210 iterations of the key derivation function are used (which is not enough, by the way. I'd recommend a cost of 12 or more.)
  • vI8aWBnW3fID.ZQ4/zo1G.q1lRps.9cGLcZEiGDMVr5yUP1KUOYTa is the salt and the cipher text, concatenated and encoded in a modified Base-64. The first 22 characters decode to a 16-byte value for the salt. The remaining characters are cipher text to be compared for authentication.

This example is taken from the documentation for Coda Hale's ruby implementation.

Is it okay to randomly generate the salt for bcrypt?

One major reason to use bcrypt is to prevent brute force attacks by requiring a lot of CPU time to calculate hashes. For your problem I would use a constant length salt, but with random values, this way each password takes the same amount of time to calculate.

From this you can cater your length of salt and number of stretching iterations to whatever you feel is secure enough, though I personally like to make sure the hash takes at least 1/2 second to generate on a really beefy server.

Generating a Random Salt and use it in Bcrypt

It is simpler and more secure to just use password_hash() and the companion password_verify() for PHP.

Use the CRYPT_BLOWFISH algorithm to create the hash. This will produce a standard crypt() compatible hash using the "$2y$" identifier.

No salt needs to be supplied and is best not to supply one, in PHP 7.x the salt option has been removed.

Example: password_hash("aPassword", PASSWORD_BCRYPT)

Understanding how salt is generated/used in bcrypt password_hash

You seem to misunderstand how password hashing and salts are supposed to work.

The salt is never sent to the client. It is generated (or manually specified) only once when the password is created. Its purpose is to randomize the output of the hash function, so that when the database gets into the wrong hands it is not possible to get users passwords by comparing the output to rainbow tables.

When the user uses his password to login the password is sent from the client to the server unhashed (but usually over https). The password comparing function then fetches the stored hash+password, gets the salt from it, appends the salt to the user input, calculates the hash and then compares that to the hash from the database.

Maybe the project you're on has a bad implementation of salts. In fact, one of the reasons using manual salts is discouraged is to prevent things like this.

So a solution could be:

let FOSUserBundle use password_hash to create the hash without
manually specifying a salt.

extract the salt from the result string and pad it with 0 to a length
of 32 chars

pass this salt to the client

Can anyone confirm, that this a real solution and not just some
coincidence ?

This is not a good solution. The only good way is to make sure password hashing is implemented the right way so you don't have to generate the salt more than once.

Do bcrypt minor version generates (2b vs 2a) generate different salt?

The Salt is just a random string, it should vary every time you generate a password hash regardless of the 2a/2b scheme. Salts should prevent people from build dictionaries of common plaintext and hash combinations.

How bcrypt compares passwords with different generated salt?

Every time when bcrypt generates salt and hash, he stores salt in generated hash.

Example

We have hash - $2y$14$i5btSOiulHhaPHPbgNUGdObga/GC.AVG/y5HHY1ra7L0C9dpCaw8u

Format (identifier) - 2y

Cost parameter - 14

Salt - i5btSOiulHhaPHPbgNUGdO

Hash digest - bga/GC.AVG/y5HHY1ra7L0C9dpCaw8u

P.S.
You can read about this here - https://github.com/ademarre/binary-mcf

Is there a practical difference between generate bcrypt salt and hash on separate function calls or auto-gen a salt and hash?

It's a common implementation in a lot of libraries where they want to use the more tedious version.

  • they insist that you have to pass in everything required to run the function
  • and they abstract the details of passing in salt and cost and versioning away in a salt string

I believe the method signature should be:

bcrypt.HashPassword("hunter2"); //using a default cost

bcrypt.HashPassword("hunter2", 15); //if we want to force a cost

But nearly every other bcrypt library does something like:

String salt = bcrypt.GenerateSalt(); //using a default cost
bcrypt.HashPassword("hunter2", salt);

String salt = bcrypt.GenerateSalt(15); //if we want to for a cost
bcrypt.HashPassword("hunter2", salt);

Because then what happens internally when they go to verify a hash, they extract the saved salt string from the stored hash:

String salt = GetSaltStringFromSavedHash(savedHash);
bcrypt.HashPassword("hunter2", salt);

And so they just love this symmetry of using HashPassword in the same way for both calls.

I disagree that any of this salt should be exposed to the user - even if it designed to be an opaque blob (i.e. $2b$15$aXN0aWxsbG92ZXlvdWtn...).

I think it should be:

HashPassword(password);
HashPassword(password, costFactor);

But that's just me; and i'm the only one.



Related Topics



Leave a reply



Submit