I am trying to understand password_hash fully in order to be able to explain it for an auditor.
Based on my searching for an answer, I understand that the password_hash() function is a wrapper for crypt(). While reading the PHP manual for predefined Constants I see that it uses PASSWORD_BCRYPT as the default integer value (basically it uses the CRYPT_BLOWFISH algorithm to hash a password).
What's confusing me is that the $options variable, if omitted, generates a random salt and the cost will be set to 10. If I supply a higher cost (for example: 12), will it still generate a random salt since I am not supplying a salt value? The reason why I am confused here is because I am not omitting the $options but instead supplying a different cost.
My other questions:
Why does increasing the cost value increase security?
How, since password_hash() is a one way hashing function, does password_verify() validate the password since the salt is random?
Is CRYPT_SHA512 stronger than CRYPT_BLOWFISH for hashing?
I find this article incredibly useful to understand how to correctly hash passwords. It explains how hashes can be cracked with various techniques if the hashes are weak, and how to hash passwords correctly to provide sufficient security.
If I supply a higher cost (say 12), will it still generate a random
salt since I am not supplying a salt value
Yes it will - as the documentation says if salt is omitted, a random salt will be generated by password_hash() for each password hashed (this means if you omit the salt value from your options array, it will be generated by password_hash() function defaultly). Moreover, the salt option has been deprecated since php 7.0.
why increases to the cost value increase security?
This is also explained in the above article in section Making Password Cracking Harder: Slow Hash Functions. The higher the cost is set to, the slower is the hash function. The idea is to make the hash function very slow, so that even with a fast GPU or custom hardware, dictionary and brute-force attacks are too slow to be worthwhile. The cost should be however set to reasonable value (based on the specs of your server), so that it doesn't cause significant time delays when verifying users' passwords.
More, is CRYPT_SHA512 stronger that CRYPT_BLOWFISH for hashing?
Read this post about their comparison.
Password hash works by using crypt() in basically a wrapper. It returns a string that contains the salt, the cost and the hash all in one. It is a one-way algorithm, in that you don't decrypt it to validate it, you simply pass the original string in with your password and if it generates the same hash for the provided password, you're authenticated.
It's best to omit the salt and let it generate one for you. If you use only one salt, it makes it easier to break all your passwords instead of just that one. Salts can be generated regardless of the cost.
Cost (an exponential value) refers to how much effort goes into generating the hash (where higher = more computing power to generate a hash). Don't set it too high or you will bog your login scripts down.
Generally speaking:
You always should apply a salt when hashing passwords, to have a different hash even if you have the same password. This increases security by "preventing" people from using rainbow tables to crack the password.
But bcrypt handles the salting on its own!
Back to your original question:
The cost is used to make it "costly" to crack the password with a dictionary/brute force attack.
Bcrypt basically hashes the password over and over, which makes it time consuming (=costly) to obtain the password to a given hash. If you try to find a password for a hash (brute force attack) you have to calculate billions of password hashes. When each hashing takes "$cost" times as long, then a brute force attack is not feasible. Even if you can calculate the hash for a potential password in milliseconds.
In simple terms:
If you have a password hash for SHA-1 (unsecure, don't use it!) with the salt (as this is usually contained in the hash) and you want to hack it then you have to hash all possible passwords + the salt and when you find the combination with the same hash, you found a possible password for this hash.
Let's say you use a good salt and a long enough password, then you need something like 1-5 seconds for a password hash. If you use the blowfish approach with cost=10 you need 10-50 seconds for a password hash.
For a single password, this is no big deal. So a directed attack for a single hash is still simple, but usually people obtain large lists of user and password combinations and they are interested to get the passwords for all of them quickly. Then this is much less lucrative for the bad guy, as he needs 10 times the CPU power to calculate all that stuff.
Related
I am using password_hash for password encryption. However there is a strange question, password_hash cost very long time. Here is a sample code.
this code will cost more than 1 second. Is that normal?
<?php
$startTime = microtime(TRUE);
$password='123456';
$cost=13;
$hash=password_hash($password, PASSWORD_DEFAULT, ['cost' => $cost]);
password_verify($password,$hash);
$endTime = microtime(TRUE);
$time = $endTime - $startTime;
echo $time;
?>
the result is :1.0858609676361
After running on 3v4l that seems perfectly normal.
Password hashing is not something you want optimize. In the words of Leigh on the hash documentation:
If you are hashing passwords etc for security, speed is not your friend. You should use the slowest method.
Slow to hash means slow to crack and will hopefully make generating things like rainbow tables more trouble than it's worth.
The default algorithm for password_hash, bcrypt, is designed to be slow.
http://en.wikipedia.org/wiki/Key_stretching
In cryptography, key stretching refers to techniques used to make a possibly weak key, typically a password or passphrase, more secure against a brute force attack by increasing the time it takes to test each possible key. Passwords or passphrases created by humans are often short or predictable enough to allow password cracking. Key stretching makes such attacks more difficult.
http://en.wikipedia.org/wiki/Rainbow_table#Defense_against_rainbow_tables
Another technique that helps prevent precomputation attacks is key stretching. When stretching is used, the salt, password, and a number of intermediate hash values are run through the underlying hash function multiple times to increase the computation time required to hash each password. For instance, MD5-Crypt uses a 1000 iteration loop that repeatedly feeds the salt, password, and current intermediate hash value back into the underlying MD5 hash function. The user's password hash is the concatenation of the salt value (which is not secret) and the final hash. The extra time is not noticeable to users because they have to wait only a fraction of a second each time they log in. On the other hand, stretching reduces the effectiveness of a brute-force attacks in proportion to the number of iterations because it reduces the number of computations an attacker can perform in a given time frame. This principle is applied in MD5-Crypt and in bcrypt. It also greatly increases the time needed to build a precomputed table, but in the absence of salt, this needs only be done once.
A full second is probably a little long - you could experiment with dropping $cost by one or two to bring it more to something like a tenth of a second, which will retain the effective protection while making the delay unnoticeable to your users.
Yes, it's normal. That's what the cost parameter is for: it allows you to tweak the iteration count, making the hash slower or faster as needed.
You should always make the hash as slow as possible and as fast as necessary. The reason being that the only feasible attack on a password hash is brute force. You want to make the cost so large that it takes prohibitively long to simple brute force all possible values. That's your only real defence against attackers with password hashing to begin with.
One whole second seems prohibitively for your own use. You should lower that cost a bit to stay within a few hundred milliseconds at most. Adjust to your target systems as needed.
To begin, password_hash is not encryption.
password_hash() creates a new password hash using a strong one-way hashing algorithm. password_hash() is compatible with crypt(). Therefore, password hashes created by crypt() can be used with password_hash().
A hash is one-way, and whatever you pass into it will always have the same end-result, however there is no way for you get the original string from the hash. This is ideal for passwords because you want to store an obfuscated version of the user's password that you can easily compare at login without actually storing what the password is. This means if the database is compromised, so long as the passwords were hashed, the attacker wouldn't get the passwords, they would get the hashed passwords which are essentially useless (you can use rainbow tables and I'm sure other techniques to get the resulting hashes, but it takes a decent amount of effort).
This leads into your original question. Why are password hashes slow? They are slow because one of the only ways to get the original string from a hash is to re-generate that hash. So if it takes 1 second to generate each hash it becomes a bigger time sink than it would have been had you used a fast hash such as md5 of a version of sha. Fast hashes are great for pretty much everything except for password storage.
Hopefully this answers your question. Just as an aside, I would strongly recommend generating a unique salt for each user and passing that in as one of the options into password_hash. This salt can be stored as plain-text in the database alongside the hashed password. Using a different salt for each password will add that into the password so a would-be attacker would have to generate a rainbow table for every salt that's in the database. At this point the attacker would likely utilize other techniques to get the passwords instead of a database breach.
I've been reading about how it works and it's really cool in how it slows down bruteforce attempts however it still doesn't feel secure.
Lets say someone stole my database data including all my user password hashes, and knows that I used password_hash to hash my passwords. Can't he just loop through my passwords with his dictionary and password_verify to gain access?
Is it good practice to add another salt before hashing the password?
Adding to the answer by #adeneo,
The point of bcrypt, pbkdf2, scrypt and modern password hashing strategies is to be slow.
Yes, if you get the resulting hash from the database (SQLi) you can just try passwords and attempt to verify each one.
However, just try passwords is a bit of an understatement. Let's look at some math.
The default cost of password_hash() with bcrypt takes approximately 0.1 seconds to hash a password. So that means it takes about 0.1 seconds to verify a password hash.
There are about 1,000,000 words in the english language. To try each one, you'd need to verify 1,000,000 times. At 0.1 seconds per, that's 100,000 seconds (~27 hours).
27 hours for 1,000,000 guesses for a single password hash that was leaked. Since each hash comes with a salt, an attacker would need to repeat these guesses for each leaked hash.
If your database had 1 million users, just to try the dictionary against the passwords would take 76,000 CPU-years (1 CPU for 76,000 years, 76,000 CPUs for 1 year, or any trade between).
To put that in perspective, md5() on a 25 GPU cluster can do about 180,000,000,000 guesses per second. To check those million dictionary entries against the million hashes from the DB, it'd take about 5.5 seconds.
137 GPU-seconds vs 76,000 CPU-years. That is why bcrypt is used.
No, if someone stole the database, they would probably already have access to everything, but say they didn't, all they would get is the hashes.
The hash can't be used in the password field, as it would be hashed again and not match the hash in the database, one would need the actual password, not the hash.
Using password_verify doesn't give you the original password, it checks wether or not a password matches the hash only, which means you would still have to have the original password and the hash to see if they match, so having only the hash gets you nowhere.
$is_correct = password_verify($password_typed_by_user, $hash_gotten_from_db); // bool
At the end of the day, nothing is secure, the hash could most likely be broken with brute force, rainbow tables or with wordlists/dictionaries etc. but that's irrelevant as it generally takes a lot of time and effort and can be done with any hash regardless of wether or not password_hash was used or not.
Using an updated hash and a strong password is the best defence, if the algorithm used to hash the password is somewhat slow, it takes longer to loop through a wordlist and hash each word. Likewise if the password is long or has special characters, it would take longer to check all the characters up to that length etc.
Adding more salts does nothing. The salt isn't a secret, it's just a custom thing you add to avoid having the hashes cracked with precomputed lookup tables, like rainbow tables.
The salt is usually stored with the hash, either in the same database in a different field, or when using PHP's password_hash it's actually just concatenated to the hash, looking something like mysalt.hash.
In general one should use random salts, to make it impossible to pre-generate tables of hashes, and that's all that's needed, the salt isn't a secret and it doesn't add security other than making the function to generate the hash somewhat unique so it can't be duplicated on a mass scale.
That is always a problem. Let's hope your users used passwords that aren't in a dictionary or in the top 100 used passwords. It's your responsibility to enforce stronger passwords that are not easily found in a dictionary.
I'm the developer of a new website built in PHP and I'm wondering what exactly is the best
thing to use for hashing. I've looked at md5 and sha1 but is there anything more secure.
I'm sorry if this is a nooby question but I'm new to PHP Security and I'm trying to make my
site as secure as possible. Also what is a salt?
Thanks,
Waseem
First off md5 and sha1 have been proven to be vunrable to collision attacks and can be rainbow
tabled easily (When they see if you hash is the same in their database of common passwords).
There are currently two things that are secure enough for passwords, that you can use.
The first being sha512. sha512 is a sub-version of SHA2. SHA2 has not yet been proven to be
vunrable to collision attacks and sha512 will generate a 512 bit hash. Here is an example of
how to use sha512:
<?php
hash('sha512',$password);
The other option is called bcrypt. bcrypt is famous for its secure hashes. Its
probably the most secure one out there and most customizable one too.
Before you want to start using bcrypt you need to check if your sever has it enabled, Enter
this code:
<?php
if (defined("CRYPT_BLOWFISH") && CRYPT_BLOWFISH) {
echo "CRYPT_BLOWFISH is enabled!";
}else {
echo "CRYPT_BLOWFISH is not available";
}
If it returns that it is enabled then the next step is easy, All you need to do to bcrypt a
password is (Note for more customizability you need to see this How do you use bcrypt for hashing passwords in PHP?):
crypt($password, $salt);
Now to answer your second question. A salt is usally a random string that you add at the end of
all you passwords when you hash them. Using a salt means if some one gets your database
they can not check the hashes for common passwords. Checking the database is called using a rainbow table. You should always use a salt when hashing!!
Here are my proofs for the SHA1 and MD5 collision attack vulnerabilities:
http://www.schneier.com/blog/archives/2012/10/when_will_we_se.html, http://eprint.iacr.org/2010/413.pdf, http://people.csail.mit.edu/yiqun/SHA1AttackProceedingVersion.pdf, http://conf.isi.qut.edu.au/auscert/proceedings/2006/gauravaram06collision.pdf and Understanding sha-1 collision weakness
The whole purpose of the salt is to slow down an attacker from comparing a list of pre-generated hashes against the target hash.
Instead of needing to pre-compute one "hashed" value for each plaintext password, an attacker needs to precompute 16384 "hashed" values for each plaintext password (2^7 * 2^7).
That kinda pales today but was pretty big when the crypt function was first developed - the computational power to pre-compute that many passwords times the number of plaintext password you suspect (dictionary) was pretty high.
Not so much today which is why we have things like shadow passwords, other core password functions besides crypt and every sysad wanting you to pick a password that would not show up in a dictionary.
If the hashes you want to generate are for passwords this is a well accepted method of implementing it.
http://www.openwall.com/phpass/
If you're planning to do this for passwords, then do not use MD5 or SHA1. They are known to be weak and insecure, even with salt.
If you're using them for other purposes (eg providing a hash of a file to confirm its authenticity, or a random hash database column to provide a pseudo-random sort order) then they are fine (up to a point), but not for passwords or anything else that you would consider needing to be kept secure.
The current best-practice algorithm for password hasing is BCrypt, with suitable salting.
And the best way to implement BCrypt password hashing in PHP is to use PHP's new password API. This API will be featured as a set of built-in functions in the next version of PHP, v5.5, due for release in the next few months. The good news is that they have also released a backward-compatibility version for users of current versions of PHP (5.3 and 5.4), so even though PHP 5.5 isn't released yet, you can start using the new API immediately.
You can download the compatibility library from here: https://github.com/ircmaxell/password_compat
Also: You asked what "salt" is. Since I've mentioned it a couple of times in this answer, I should address that part of the question too.
Salt is basically an additional string added to the password when hashing it, in order to make it harder to crack.
For example, an attacker may know in advance what the hashed value is for a given password string, or even a whole lot of given password strings. If he can get hold of your hashed data and you haven't used a salt, then he can just compare your hashes against his list of known passwords, and if any of your users are using an easy to guess password, they'll be cracked in seconds, regardless of what hashing method was used.
However, if you've added a secret extra string to the password when you hash it, then the hashed value won't match the standard hash for the original password, thus making it harder for the attacker to find the value.
The good news is that if you're using the API I mentioned above, then you don't need to worry too much about the details of this, as the API handles the salting for you.
Hope that helps.
I want to use Bcrypt for the password encryption in my systems. But all the examples are something like this:
$password = $_POST['password'];
$salt = substr(str_replace('+', '.', base64_encode(sha1(microtime(true), true))), 0, 22);
$hash = crypt($password, '$2a$12$'.$salt);
This looks pretty safe to me, but I was wondering, in each example, nobody hashes the password before using Bcrypt.
Due to the unique salt, Rainbow tables shouldn't be able to crack all the passwords at once. But in case the hacker takes one record and creates a rainbow table with the salt of that particular record, he should be able to crack a weak password.
So if someone takes a weak password (let's say 'foo'), it would be safer to hash it first with SHA-512 before using Bcrypt. Am I right? Or is this just looking safer?
Actually the answer has to be no, it doesn't make the hash significant stronger in a cryptographically sense. As you probably know, bcrypt (although the function to use is named crypt) is a hash function itself, not an encryption function.
In bcrypt you pass a cost factor, which defines, how many iterations will be done (normally hundreds of them). That slows down calculation of the hash, what makes brute force attacks impracticable. Using SHA-512 before, will only add one iteration more.
What you said about the salt is correct, but of course if you have to build a rainbow table for each password, you will simply brute force until you have found a match, no need to build the whole rainbow table.
If the attacker has control over database and code, an additional SHA-512 will help nothing at all (only a single iteration more). If he has only the database without code (SQL-Injection), then he will recognize the bcrypt hash. He can now brute force with bcrypt, but because of the SHA-512 there aren't any weak passwords. It's like the SHA-512 hash would be the password to crack, so a dictionary is of no use. This is security by obscurity, but will be effective as long as the code is not known. You can get the same effect easier, by adding a fix hard coded salt (key), before using bcrypt with the unique salt.
crypt() is a one-way string hash, not an encryption mechanism. To use an SHA-512 hash, you have to use the hash() function. Bcrypt requires a PHP extension. For storing passwords, why do you want to make them reversible, rather than just hashing them? That's less secure -- if someone gets your key and DB, they have all the passwords, but a table of SHA512 hashes is pretty useless.
Hashing first won't help. A bad password is one that is deemed more probable by an attacker and placed earlier in his list of passwords to try.
Bcrypt incorporates salt to eliminate pre-computed lookup tables (a Rainbow Table is one example). Why would an attacker build a rainbow table for a single record? No, when attacking a salted password, an attacker simply works through his ordered list of most likely passwords, repeating the hash algorithm to see if it matches.
How far he can work through that list depends on how long the hash algorithm takes to execute. Bcrypt controls that with the "cost" factor—12 in your example (which is okay, but probably the minimum I'd use). An extra SHA-512 round doesn't add anything to that. You are already performing 4096 expensive bcrypt iterations. Adding 1 cheap SHA-512 iteration is negligible.
If you choose the first password on the list, it will be broken in a fraction of a second. If you pick the billionth password, the attacker won't break it for a few decades.
bcrypt already uses salt, and what it's doing internally is quite a bit stronger than SHA512. Adding an iteration of SHA512 (and/or an extra layer of salt) on top of bcrypt will not give you a significantly stronger result. If the two functions interact in the wrong way, combining them in this way may in fact give you a hash function that is weaker.
function oneWayEncrypt($string) {
$salt = md5($string."yHuJ#8&6%4#%([#d-]");
$salt2 = md5($string."#!#&+-)jU#[yT$#%");
$string = hash('sha512',"$salt$string$salt2");
return $string;
}
Using SHA-512 is a good idea to get a cryptographically strong hash, but your choice of a salt does not add much extra security. In particular, a salt is only good if its value is random and cannot be predicted in advance. This prevents an attacker from precomputing a table of known hashes with which to try to attack your database. If the salt is known, then the attacker can just precompute a table of hash values with the salt hardcoded in.
In your case, the salt is essentially known to the attacker because it's deterministically computed from the input string. If I wanted to attack your system, I could iterate across a bunch of known strings, (deterministically) compute the salt for each string, then compute the SHA-512 hash of the salted string and store it in a table. From this, I could invert a hash to a password for any string I happened to precompute.
If you want a better security system, instead consider using a salt that's randomly-generated and then stored alongside the resulting hash. That way, no matter what tables I precompute, there's a slim chance that the table will be useful because I won't necessarily have computed the tables for all possible salts. Essentially, each random bit in your salt doubles the amount of work I have to do, so if you pick a good random salt (say, 128 bits) then there's no feasible way I could do a precomputation attack. I'd have to attack SHA-512, a hash assumed to be cryptographically secure (the name means "Secure Hash Algorithm"), to break your system.
How secure for what?
For storing hashed passwords? - Use random salts, different for every password.
For signing cookies? - Use HMAC, a Hash-based Message Authentication Code.
You're saying that you want to use it for storing passwords in DB and cookies, both of which should be done using other proved techniques, see above. Don't try to reinvent the wheel.
When you ask how secure something is, you have to know not only what way are you going to use it but also what kind of attack do you want it to be secure against. Things are not secure in a vacuum.
Also, don't assume that SHA-512 is better for your application just because it has more bits. Read the paper Preimage Attacks on 41-Step SHA-256 and 46-Step SHA-512 by Yu Sasaki, Lei Wang, and Kazumaro Aoki (PDF) to see that for certain applications some shorter hashes can be actually more secure than SHA-256 and SHA-512 because there are no known preimage attacks that would brake so many rounds as for SHA-256 and SHA-512.
Although SHA-512 is a good choice for a cryptographic hash function in general, it still might be too easy to compute: SHA-512 is computationally fast enough to process 154 MB/s. You should better choose a cryptographic hash function that is computationally slower like bcrypt that can be slowed down with a cost factor.
Additionally, use a random and unique salt for each hash operation and store it together with the hash to be able to reproduce the hash for comparison.