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 thebcrypt
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 charspass 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
How to Get Rid of MySQL Error 'Prepared Statement Needs to Be Re-Prepared'
PHP Array Behaving Strangely with Key Value 07 & 08
How to Get Ruby Syntax Highlighting in PHPstorm
How to Get the Total Number of Rows of a Group by Query
How to Detect a Malformed Utf-8 String in PHP
Algorithm That Will Take Numbers or Words and Find All Possible Combinations
Import File Size Limit in PHPmyadmin
How to Fix Stream_Socket_Enable_Crypto(): Ssl Operation Failed with Code 1
How to Set Lifetime of Session
Uploading in Codeigniter - the Filetype You Are Attempting to Upload Is Not Allowed
How to Embed Images in HTML Email
Convert PDF to Jpeg with PHP and Imagemagick
Caching Http Responses When They Are Dynamically Created by PHP