I know the PHP function, password_hash outputs the algorithm, cost, salt, and hash all in one string so password_verify can check a password.
Sample output from PHP page:
$2y$10$.vGA1O9wmRjrwAVXD98HNOgsNpDczlqm3Jq7KnEd1rVAGv3Fykk1a
so the $2y$ represents the algorithm, the 10 represents cost.
But how does password_verify separate the salt from the hash? I don't see any identifier separating the two afterwards.
For the bCrypt version of Password Hash.
Bcrypt has a fixed-length salt value. The crypt function which is what PHP calls internally when you're utilizing password_hash()/password_verify() with the default algorithm has a a 16 byte salt. This is given as a 22 characters of the custom base64 alphabet A-Za-z/. then it decodes the string into bytes as 22 B64 characters encode 16.5Bytes there is an extra nibble of data that is not taken into account.
For all other hashes the salt value is a defined set of bytes which are of course encoded into ASCII safe b64 and put after the $ sign and then the verifying function would only have to split the string into parts via the delimiter $ and then go for the third set of characters get the substr(0,B64_ENCODED_HASH_ALGORITHM_SALT_LEN). After that it would then pass the parameters it also got from the split string and pass those back into the password_hash function along with the password to check.
The string it gives you is defined by the hashing algorithm's standard in most cases but is almost always something to the pattern of
$<ALGORITHM_ID>$<COST_IN_FORMAT>$<BASE64_ENCODED_SALT><BASE64_ENCODED_HASH>$
Related
I was looking for answers on BCRYPT specific resources but found the anwser within PHP's documentation for crypt.
I have an issue whereby I need to do some REGEX to clean a BCRYPT hash (generated by PHP password_hash function using PASSWORD_BCRYPT). I want to be able to know the characters that could theoretically appear in the BCRYPT hash so that REGEX can remove all other characters from a string.
I have read all about various bits of BCrypt, it's history and its development but I have not come across anywhere that states for canon what a BCRYPT hash can contain.
Current understanding is:
*, 0-9, a-z, A-Z, $, . \
Does BCRYPT contain any ascii character? (edit: no) I see it contains many but going through the many BCRYPT hashes I can find is not a good methodology for being sure. For example, BCRYPT hash does NOT seem to contain = or ¬ or a few other characters that, -for want of a better description- have small UTF-8 definitions.
This is using a PHP interface if that changes how BCRYPT outputs hashes.
I found the answer on the PHP Crypt Function page:
CRYPT_BLOWFISH - Blowfish hashing with a salt as follows: "$2a$", "$2x$" or "$2y$", a two digit cost parameter, "$", and 22 characters from the alphabet "./0-9A-Za-z". Using characters outside of this range in the salt will cause crypt() to return a zero-length string
So this PHP/BCRYPT Hash will use characters from:
$./0-9A-Za-z range.
This question already has answers here:
Why is the PHP crypt() function returning the same thing for two different strings?
(3 answers)
Closed 7 years ago.
I was using PHP's crypt() function to encrypt the password before storing into the database.Now if the password contains number then all passwords with same sub string generates the same encryption. For example all the below passwords generate the same encryption.
echo crypt('abcdefg123','mykey').'<br>';
echo crypt('abcdefg123','mykey').'<br>';
echo crypt('abcdefg123456','mykey').'<br>';
Encrypted password result is
myeWT99Ku6TaM
What am I doing wrong? or is it a bug?
crypt function takes salt as the second argument. salt has special formats described here.
You have provided a salt which is recognized as standard DES algorithm.
The standard DES-based crypt() returns the salt as the first two characters of the output. It also only uses the first eight characters of str, so longer strings that start with the same eight characters will generate the same result (when the same salt is used).
Provide the proper salt. For example, try this for MD5:
echo crypt('abcdefg123','$1$mykeyabcd$').'<br>';
echo crypt('abcdefg123','$1$mykeyabcd$').'<br>';
echo crypt('abcdefg123456','$1$mykeyabcd$').'<br>';
I think it's because of salt.
By default you propably use CRYPT_STD_DES hash type in crypt function and this type works with two character salt, but you use 5 character salt.
Using invalid characters in the salt will cause crypt() to fail.
http://php.net/manual/ru/function.crypt.php
Reading about generating salt using Cryptographically Secure Pseudo-Random Number Generator (CSPRNG). This salt then will be appended to a string that needs to be hashed.
However, the salt generated by CSPRNG function (for PHP I'm using openssl_random_pseudo_bytes) is actually binary data.
Confused about how I should append this binary data to a string, I saw this PHP example for creating hash. It encodes binary data.
So I just wanted to know if that is what I need to do. I need to encode salt to get a string. Then I can append that salt to a string that needs to be hashed. Or are there other ways of adding salt to a string?
note I'm not hashing a password
If you need to hash a password, please use password_hash() and password_verify(), and probably add password_needs_rehash() - see http://de2.php.net/password_hash.
You might notice that these functions are available since PHP 5.5.0 - if you are using an earlier version of PHP, you can add this compatibility library to make it work with PHP starting at 5.3.7.
It can't get very much easier than that.
It's probably best to first convert your string into binary data using an encoding. UTF-8 encoding is probably best for most use cases. Don't forget to (at least) document which character encoding is used.
Now concatenate the salt and the encoded string. Again, you need to (at least) document the size of the salt. Please make sure you use concatenation of bytes, not strings. Bytes can have any value, including invalid characters, control characters etc.
After the concatenation you can feed the resulting byte array into the hashing function.
If you have trouble with byte concatenation in PHP, you could use hexadecimal values instead. But don't forget to convert them back into bytes before feeding them into the hash method.
What is the character set of the output given by crypt() using md5 with salt.
By hash, I mean just the 22 characters after "$1$ "8 random characters"$ ". So I wanted to know what type of characters does 22 hashed character contains?
I was looking for this and have found a few questions that touched on this but nobody seems to have a definitive answer, nobody except the code of course and, this python implementation of the same:
http://pythonhosted.org/passlib/lib/passlib.hash.md5_crypt.html
Based on this, it seems both the salt, and the hash itself are encoded with the following regex char set: [./0-9A-Za-z]
I would expect these to output the same since they are all trying to be compatible with the same shadow password utilities.
According to the crypt() documentation, the salt needs to be 22 base 64 digits from the alphabet "./0-9A-Za-z".
This is the code example they give:
crypt('rasmuslerdorf', '$2a$07$usesomesillystringforsalt$');
The first confusing part is that salt has 25 characters, not 22.
Question #1: Does that mean the salt is supposed to be longer than 22 characters?
Then I tested the function myself and noticed something. If I use a 20 character salt, I get this
// using 20 char salt: 00000000001111111111
crypt('rasmuslerdorf', '$2a$07$00000000001111111111$');
// $2a$07$00000000001111111111$.6Th1f3O1SYpWaEUfdz7ieidkQOkGKh2
So, when I used a 20 character salt, the entire salt is in the output. Which is convenient, because I do not have to store the salt in a separate place then. (I want to use random salts). I would be able to read the salt back out of the generated hash.
However, if I use a 22 character salt as the documentation says, or a longer one, the salt is cut off at the end.
// using 22 char salt: 0000000000111111111122
crypt('rasmuslerdorf', '$2a$07$0000000000111111111122$');
// $2a$07$000000000011111111112uRTfyYkWmPPMWDRM/cUAlulrBkhVGlui
// 22nd character of the salt is gone
// using 25 char salt: 0000000000111111111122222
crypt('rasmuslerdorf', '$2a$07$0000000000111111111122222$');
// $2a$07$000000000011111111112uRTfyYkWmPPMWDRM/cUAlulrBkhVGlui
// Same hash was generated as before, 21 chars of the salt are in the hash
Question #2: So, what exactly is the proper length of a salt? 20? 22? Longer?
Question #3: Also, is it a good idea to read the salt out of the hash when it is time to check passwords? Instead of storing the salt in a separate field and reading it from there. (Which seems redundant since the salt seems to be included in the hash).
Blowfish salts should be 22 chars long (including the trailing $, so 21) - you can double check with var_dump(CRYPT_SALT_LENGTH), I can't verify this now but my guess is that less chars will return an error and more chars will be truncated.
Regarding your third question: yes, you should read and check the hash using the embedded salt (and cost) parameters from the hash itself.