Correct way of using SHA512 encryption - php

I am trying to use SHA512 algorithm in PHP using function crypt.
My salt:
$salt = base64_encode(substr(str_shuffle("./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz012345‌​6789"), 0, 12));
I get something like this:
Q4CALzJNenFaZnNK
I am not sure why I get lenght 16 while I specified 12.
And to hash the password, I use this:
$hashed = crypt('myPassword', '$6$rounds=5000000$'.$salt);
The output is something like that:
$6$rounds=5000000$Q4CALzJNenFaZnNK$9QTP6C.BZ9Z.U85UIEAVX1dEIdShHFoYGgTMvgv9Cx/XZY1mK/n2rY4FuHSoigjgIXfqGZftZSxrrF.cDBzt8/
Lenght: 121
So my question is it ok to store this password in the database or should I strip $ signs as I saw in few examples?
Also I already store passwords in VARCHAR(255) and I was wondering if I could make the output twice as long, i.e. near 255 characters?
Is this way more secure than for instance Blowfish?
My findings:
The length of a hashed password is not that important as I first thought (60 characters is well enough to store instead of 128 or 256).
It is best to use password_hash function and forget about generating your own salt - php.net know what they do.
So I ended up hashing passwords this way:
$hash = password_hash($password, PASSWORD_BCRYPT, array("cost"=>15));
PASSWORD_BCRYPT is Blowfish algorith with the default cost of 10 (times it runs the algorithm or something). 10 is a good number to slow down the brute force attacks. I wanted to show how you can change the cost manually.

You get a larger salt back because of base64_encode will enlarge your 12 character string to a 16 character string (it's encoding does that)
You can store the string fully in one field but if you want easy access to the salt, you could store the salt in another field. (You need the salt again to recheck if the user password input is correct - the salt only makes sure that a hash of the same password wouldn't give the same hash)
Is SHA512 safer as Blowfish? As erickson on stackoverflow said, they are both good enough for the purpose

You have this:
$salt = base64_encode(substr(str_shuffle("./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"), 0, 12));
You can simply remove the base64_encode from this to get a 12 character salt. Also note that in your version you have some non-ascii, non-printable characters between the 5 and the 6. That probably causes the binary output. Try this:
$salt = substr(str_shuffle("./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"), 0, 12);

So my question is it ok to store this password in the database
Yes, just store the whole thing in the database, including the $6$ and the rounds=5000000. This makes it possible to switch to another hash type in the future, and you can just use crypt on the whole password to check it.

Also I already store passwords in VARCHAR(255) and I was wondering if I could make the output twice as long, i.e. near 255 characters?
In principle longer is better, so SHA512 is better than SHA256. However, a 120 character hash is already pretty long and there is no advantage to make it even longer. You can increase the length of the salt, but don't try to make the hash longer by appending another hash or something like that.

Related

Understanding bcrypt salt as used by PHP password_hash

I have some trouble to understand how bcrypt uses the salt. I know what the salt is good for but I do not understand how the salt value is used exactly.
Problem 1: What is the correct salt length?
All sources I found say, that the salt has a length of 22 and that it is stored together with the algorithm, the costs and the actual hash value in the result string.
However, all implementations I found, use a salt with length 32. For example the FOSUserBundle used by Symfony used the following code to creat the salt:
$this->salt = base_convert(sha1(uniqid(mt_rand(), true)), 16, 36)
Since a sha1 hash is 32 chars long, the generated salt also has a length of 32. Is this just a lazy implementation, skipping the code to trim the string to a length of 22 because this is done by bcrypt it self? Or are 32 chars necessary for some reason?
Problem 2: Is a salt length of 22 really correct?
In the following example it seems, that only the first 21 chars of the salt are saved in the result string. Passing these 21 chars as salt to password_hash will result in an error, but padding a 0 will work:
$s = 'password';
$salt = 'salt5678901234567890123456789012';
$salt_prefix = 'salt567890123456789010'; // first 21 chars of salt + 0
$h1 = password_hash($s, PASSWORD_BCRYPT, array('salt' => $salt));
$h2 = password_hash($s, PASSWORD_BCRYPT, array('salt' => $salt_prefix));
echo $h1 . PHP_EOL;
echo $h2 . PHP_EOL;
//Result
$2y$10$salt56789012345678901uTWNlUnhu5K/xBrtKYTo7oDy8zMr/csu
$2y$10$salt56789012345678901uTWNlUnhu5K/xBrtKYTo7oDy8zMr/csu
So, one needs to pass a salt with at least 22 chars to the algorithm but the 22nd chars seems to be useless. Is that correct? What is the sense of the 22nd char if it is not used at all?
Problem 3: Why not specify the salt manually?
In the PHP function password_hash using a manual hash is deprecated. Instead one is encouraged to let password_hash automatically, since would be safer.
I understand that using a "weak" salt or the same salt for all passwords can lead to risks due to rainbow tables. But why is it safer to use the auto-generated salt in general?
Why is it safer to use the auto-generated salt instead of manual salt, that is generated like this:
$this->salt = base_convert(sha1(uniqid(mt_rand(), true)), 16, 36)
Problem 4: Is there any replacement for password_hash that still allows the usage of a custom salt?
Due to the implementation of project I am working on, I need to control the salt, that is used to generate a password hash. This can be changed in the future, but right know it is necessary to set the salt manually. Since this feature is deprecated in password_hash, I need some alternative to generate the hash. How to do this?
EDIT:
Just a short explanation why I need to control the salt: The password is not only used to login into the web app directly, but also to connect to the app via a REST API. The client requests the salt from the server and uses it (algorithm and costs are known) to hash the password, the user entered on the client side.
The hashed password then send back to the server for authentication. The purpose is to not send the password in plain text. To be able to generate the same hash on the client as on the server, the client needs to know which salt the server used.
I know that a hashed password does not add any real security, since the communication is already uses HTTPS only. However this the way the clients currently operate: Authentication is granted if the client send back the correct password hash.
I cannot change the server side without breaking thousands of existing clients. The clients can be updated sometime in the future, but this will be a long process.
Since this is done, I need to follow the old process, which means I need to be able to tell the clients the salt.
However I do not need to generate the salt myself. I am totally fine if PHP knows the most secure way how to do this. But I do need to get/extract the salt someway, to send it to the clients.
If I understood everything correctly, I could just let password_hash do the work and then extract the chars 7-29 from result string. Is this correct?
Problem 1: What is the correct salt length?
All sources I found say, that the salt has a length of 22 and that it is stored together with the algorithm, the costs and the actual hash value in the result string.
If all sources say it, there's shouldn't be a reason for you to question that ...
There's no universal salt size, it depends on the algorithm and for bcrypt, it is 22 ... although there's a catch. The necessary size is actually 16 bytes, but that is actually Base64-encoded (*).
When you Base64-encode 16 bytes of data, that will result in a 24-character length ASCII string, with the last 2 characters being irrelevant - that becomes 22 when you trim those 2 irrelevant ones.
Why are they irrelevant? Your question is broad enough already ... read the Wikipedia page for Base64.
* There are actually a few Base64 "dialects" and the one used by bcrypt is not quite the same as PHP's base64_encode().
However, all implementations I found, use a salt with length 32. For example the FOSUserBundle used by Symfony used the following code to creat the salt:
$this->salt = base_convert(sha1(uniqid(mt_rand(), true)), 16, 36)
Since a sha1 hash is 32 chars long, the generated salt also has a length of 32. Is this just a lazy implementation, skipping the code to trim the string to a length of 22 because this is done by bcrypt it self? Or are 32 chars necessary for some reason?
That line will result in a 31-character string, not 32, but that's not actually relevant. If you provide a longer string, only the necessary part of it will be used - those last characters will be ignored.
You can test this yourself:
php > var_dump(password_hash('foo', PASSWORD_DEFAULT, ['salt' => str_repeat('a', 22).'b']));
string(60) "$2y$10$aaaaaaaaaaaaaaaaaaaaaO8Q0BjhyjLkn5wwHyGGWhEnrex6ji3Qm"
php > var_dump(password_hash('foo', PASSWORD_DEFAULT, ['salt' => str_repeat('a', 22).'c']));
string(60) "$2y$10$aaaaaaaaaaaaaaaaaaaaaO8Q0BjhyjLkn5wwHyGGWhEnrex6ji3Qm"
php > var_dump(password_hash('foo', PASSWORD_DEFAULT, ['salt' => str_repeat('a', 22).'d']));
string(60) "$2y$10$aaaaaaaaaaaaaaaaaaaaaO8Q0BjhyjLkn5wwHyGGWhEnrex6ji3Qm"
(if the extra characters were used, the resulting hashes would differ)
I'm not familiar with that FOSUserBundle, but yes - it does look like it's just doing something lazy, and incorrect.
Problem 2: Is a salt length of 22 really correct?
In the following example it seems, that only the first 21 chars of the salt are saved in the result string. Passing these 21 chars as salt to password_hash will result in an error, but padding a 0 will work:
$s = 'password';
$salt = 'salt5678901234567890123456789012';
$salt_prefix = 'salt567890123456789010'; // first 21 chars of salt + 0
$h1 = password_hash($s, PASSWORD_BCRYPT, array('salt' => $salt));
$h2 = password_hash($s, PASSWORD_BCRYPT, array('salt' => $salt_prefix));
echo $h1 . PHP_EOL;
echo $h2 . PHP_EOL;
//Result
$2y$10$salt56789012345678901uTWNlUnhu5K/xBrtKYTo7oDy8zMr/csu
$2y$10$salt56789012345678901uTWNlUnhu5K/xBrtKYTo7oDy8zMr/csu
So, one needs to pass a salt with at least 22 chars to the algorithm but the 22nd chars seems to be useless. Is that correct? What is the sense of the 22nd char if it is not used at all?
It's not really irrelevant ... pad it with e.g. an 'A' and you'll see a different result.
I can't explain this properly to be honest, but it is again caused by how Base64 works and because in the resulting hash, you actually see something similar to this (pseudo-code):
base64_encode( base64_decode($salt) . $actualHashInBinary )
That is, the (supposedly) Base64-encoded salt is first de-coded to raw binary, used to create the actual hash (again in raw binary), the two are concatenated and then that whole thing is Base64-encoded.
Since the input salt is actually the 22 relevant out of a 24-size full length, we actually have an incomplete block at the end, which is completed (filled?) by the beginning of the raw hash ...
It is a different thing to concatenate 2 separate Base64-encoded values, and to concatenate the raw values before Base64-encoding them.
Problem 3: Why not specify the salt manually?
In the PHP function password_hash using a manual hash is deprecated. Instead one is encouraged to let password_hash automatically, since would be saver.
I understand that using a "weak" salt or the same salt for all passwords can lead to risks due to rainbow tables. But why is it saver to use the auto-generated salt in general?
Simply put - the salt needs to be cryptographically secure (i.e. unpredictable), and PHP already knows how to do that, while chances are (overwhelmingly) that you don't.
Unless you have an actual hardware CSPRNG (that PHP isn't already configured to use), the best thing you can do is to leave PHP to automatically generate the salt anyway.
Yet, here we are, you obviously wanting to do the opposite (for whatever reason) and making it less secure in the process - a lot of people do that.
This is why the salt option is deprecated - to protect you from yourself. :)
Why is it saver to use the auto-generated salt instead of manual salt, that is generated like this:
$this->salt = base_convert(sha1(uniqid(mt_rand(), true)), 16, 36)
As I said, the salt needs to be unpredictable. In this specific example - none of the functions used are unpredictable, even mt_rand().
Yes, mt_rand() is not actually random, despite what its name implies.
Problem 4: Is there any replacement for password_hash that still allows the usage of a custom salt?
Due to the implementation of project I am working on, I need to control the salt, that is used to generate a password hash. This can be changed in the future, but right know it is necessary to set the salt manually. Since this feature is deprecated in password_hash, I need some alternative to generate the hash. How to do this?
You don't.
There's absolutely zero reason for your project to dictate how the password_hash() salt is generated. I don't know why you think it is necessary, but it 100% isn't - it would make no sense.
Though, ultimately - this is why deprecations are put in place before something is removed. Now you know the salt option will be removed in the future, and you have plenty of time to refactor your application.
Use it wisely, don't try to replicate deprecated functionality. You should be working in the opposite direction - ask how to separate the two without breaking your application.
You can use crypt with blowfish. It still acccepts custom salt in 2023. Not recommended to use the same salt for password, but for identifiers e.g. email addresses it is better than nothing or a checksum algorithm.

What should be the minimum length of a salt and is it always unique with openssl_random_pseudo_bytes

I'm working on storing the password of my users in my database in a secure way. I read a bit and I was recommanded to use openssl_random_pseudo_bytes to generate my salt. I do:
bin2hex(openssl_random_pseudo_bytes($thenumberIwanttouse, $cstrong));
It works, but I was wandering two things:
-What should be the lenght of my salt and in my database, if my salt have 30 character for exemple, I would just need a varchar field with a length of 30?
-Will openssl_random_pseudo_bytes always generate a new salt?If not, should it mean that I need to compare my salt to all other existing salt in my database to make sure that there aren't any duplicate?
The raw format length of the salt is 16 bytes and it is NOT hex-encoded. However, you are not supposed to generate it yourself, nor to have a separate field for salt in the database!
password_hash() will automatically generate a salt (and it will do that better than you could), and then store it in the hash itself - that's just how the algorithm works.
You shouldn't be using anything but password_hash() and password_verify() to make the whole thing work.
In addition, there are better alternatives to openssl_random_pseudo_bytes() for generating random data, such as random_bytes() under PHP7, or it's backport for older PHP versions - the random_compat package.

Does Bcrypt require anything to make it secure

I've been looking at encryption methods for a while now and what I've found so far is that Bcrypt is one of the best ways to do so right now. What I don't get yet is the way that Bcrypt works precisely. I understand that it takes longer to solve which is why it makes bruteforcing so hard.
But I don't understand whether it requires other measures such as a random salt to make it secure. Especially after reading about md5 and how having a random salt is almost mandatory before a hash becomes secure.
The sample code I found on php.com is this:
$options = [ 'cost' => 12, ];
echo password_hash("rasmuslerdorf", PASSWORD_BCRYPT, $options)."\n";
?>
I'm guessing the cost simply makes it so it runs through the function 12 times to encrypt the word "rasmuslerdorf". And the "PASSWORD_BCRYPT" selects the Blowfish algorithm.
Are there any big differences between PASSWORD_DEFAULT and PASSWORD_BCRYPT?
Is it enough for me to use the default function to encrypt the password on registration. And than compare the password after encrypting it that the user enters on login to the encrypted password in the database?
I'm guessing the cost simply makes it so it runs through the function 12 times to encrypt the word "rasmuslerdorf"
No, the cost parameter effects an exponential amount of work to be done.
But I don't understand whether it requires other measures such as a random salt to make it secure.
The password_hash() function automatically generates a random salt whenever you run it; alternatively, a custom salt can be passed via the options:
password_hash('bla', PASSWORD_BCRYPT, ['salt' => ...]);
By passing a custom salt you're assumed to know what you're doing. For all practical purposes you should be safe to stick with automatically generated salts.
Are there any big differences between PASSWORD_DEFAULT and PASSWORD_BCRYPT?
The PASSWORD_DEFAULT algorithm is provided to future-proof your code by always using the strongest algorithm available at that time (provided you update PHP). The notable difference is in storage requirements; whereas Bcrypt always uses 60 characters, you need to cater for bigger storage (e.g. 255 characters) for whatever will be used in the future.
And than compare the password after encrypting it that the user enters on login to the encrypted password in the database?
Please look at password_verify() for examples on how to verify the password a user enters.
The Bcrypt algorithm is the default algorithm. So, PASSWORD_DEFAULT and PASSWORD_BCRYPT are the same. The default algorithm can be configured in your php.ini file, but if you did not know that then it is most likely still the default.
The cost number is not how many times it is hashed. How many times it is hashed is calculated by using the formula, 2^cost. So, if the cost is 12 then it will be hashed 2^12 times (4096).
You do not have to think about salts when using the function. It creates the salt itself and appends it to the output hash:
$[algorithm]$[cost]$[salt 22 chars][rest is the hash]
You should never touch the hash, when using the password hashing functions. To verify a password against the has you should use password_verify().
The function you are using was made so that people can hash passwords without knowing what is happening in the background. That is a good thing, because when it comes to hashing passwords it is very easy to get it wrong, even if you think you know what you are doing.

How secure is encryption for passwords with crypt() in PHP?

I'm using crypt() encryption in PHP like this:
<?php
$password = sanitizing_func($_POST['password']);
$var = crypt($password, 'ab');
?>
How Secure is this?
Found a better solution here: openwall phpass
Thanks to Edward Thomson
It's less secure than if you just use crypt the way it was designed, with the password as the first argument and the salt as the second.
Now you're encrypting known plaintext using the user's password as the salt. If your system uses an MD5 crypt, then you've just limited the salt space to 12 characters, so you're truncating the space of users passwords to twelve characters. Worse still, my system requires me to use a prefix on the salt in order to specify my crypt, or else I get old school crypt, meaning you have two characters for the salt. So you've limited the possible length of a users password to two characters. Plus there's no point in even running crypt at this point, you might as well just store their two character password, since the salt is prefixed to the ciphertext so that subsequent calls to crypt can pass the same salt.
Also, you're limiting the character space of the password by using it in the salt, since the character space of the salt is limited to A-Z, a-z, 0-9, ".", "/". Even if you switch the arguments around from your code example, you're using the same salt data for every call. This means that every password has the same salt. So if your password table is exposed, it becomes less computationally expensive to crack using a dictionary attack.
In other words, swapping the password and salt arguments is a fatal mistake.
Finally, there's simply no reason to call crypt twice. If you want better encryption, use a better algorithm, don't call it more frequently. For example, if you're using a DES crypt, then it's still an ancient algorithm no matter how many times you call it. (I also seem to remember reading that multiple passes of an algorithm may inadvertently produce weakened ciphertext. But I don't have Schneier in front of me.)
What you want to do is the industry standard: use a strong crypt, pass the password as the first argument and random salt data in as the second argument and make sure that you're passing the maximum allowable number of bytes in for the salt.

How can I make MD5 more secure? Or is it really necessary?

I'm storing username and password in a MySQL database and have them hashed using MD5. However, I'm only using the standard PHP function without any modification. Now, I read that MD5 is broken. How are you doing it? Do you run it several times through a different hash mechanism or add some form of salt?
I'm amazed how people jump on the bandwagon of "damn, it's broken, I won't use it!", don't do the same mistake.
You can't make the MD5 better. Even using SHA-1 is vulnerable to same type of attacks as MD5.
Using bcrypt will use A LOT more CPU than MD5 and SHA algorithms.
MD5 is designed to be fast, same as SHA. bcrypt isn't and it allows for more permutations, which makes it harder for someone to try to decrypt the original string.
You need to know why MD5 is considered "broken".
Because it's fast to calculate a rainbow table of passwords up to 6 characters in length.
Using today's computing power, one can create an array of characters and MD5 all permutations and map them to the original string. That's how you get a rainbow table. If someone downloads your database and then compares passwords to their rainbow table - they can obtain users' original password. Reason why this is dangerous is because people use same passwords for many things - including paypal and other money processing service. That's why you use so-called salt. That makes it even harder to obtain the original string, so salting your users' passwords (let's say by reversing them and MD5-ing the reversed input) will make it harder for the attacker to revert the hash to original string.
Because of collisions.
What's a collision? If you give hashing function two different strings and it returns the same hash - that's a collision. How does it translate to web and hashing passwords for logins? If you have the same hash for user1/password1 and user2/password2 - they could log on as someone else. That's where collisions play the role in security.
Reason why MD5 is considered broken is because MD5 returns same hash for strings that differ in small percentage. And it's not easy to calculate what that string might be!
From mathematical point of view - yes, it's "broken" because if your string has 100 chars and it differs from other string in 10 chars (10% difference) - you get the same hash.
What applies for MD5 applies for ALL hashing algorithms. In the end, all of them don't have infinite number of possible hashes.
However, some of them (like MD5) have less possible hashes and execute faster.
In the end, if someone got to your database - you have a bigger problem than using MD5 instead of bcrypt or SHA1.
Add a salt to each password stored that's not equal for every password
Simply use MD5("yoursite.com".$string);
MD5 is not decryptable. The only possible way to crack it is through hash tables that brute force everything. If you add a random string that only you know they cant crack it.
If you're worried about password security then you should use SHA1() (or alternative) rather than MD5(). Whilst MD5 is not decryptable, it can be beaten by either rainbow tables or matching the hash.
Salts will work against rainbow table but not against matching the hash which has been achieved with MD5.
There are a couple of things you should do.
Use SHA instead of MD5. SHA is more cryptographically secure than MD5. The more bits the better!
Use a salt. This makes rainbow table attacks more difficult.
Strengthen your key by calculating the hash like as follows:
:
function strenghtened_hash( $password, $salt, $n ) {
$crypted = sha( $password . $salt );
for( $i = 0; $i < $n; $i++ ) {
$crypted = sha( $crypted . $password . $salt );
}
return $crypted;
}
Now you should be in good shape!
You might be better off using using bcrypt for password storage to prevent rainbow-table attacks in case the bad guys get hold of your DB.
At the very least, dump MD5 (although computationally fast, not very secure these days) and use something a little more secure like SHA256 with a long salt.
Switch to a different hash mechanism (you can do it incrementally as people log in) and definitely use a (different for each user) salt!
You can use a thing called a salt. It means that you also save this salt into you database. It's a random string which is more or less long and is unique for each user.
Then, to check the password, you do something like this:
<?php
$crypted = md5($salt.$passwordFromForm);
if($crypted == $passwordFromDB) {
// user logged on
}
?>
You can make MD5 or any hashing function more strong by a method called "loop-hashing" i wrote about , read it here ,Good method to encrypte data, , using a loop "for" or "while" to encrypte password a lot of times with a random generated key number , really it's strong and so easy , so won't be scare from crackers again , no one can crack an encrypted "loop-hash" at the moment with the available databases .

Categories