Reputation: 5728
I have just simple question in my mind for community . Once upon a time when i started programming i used md5 for hashing password, than later found that md5 can be cracked easily and i should use salt to make it secure.
Than i had no faith in md5 and wanted to use sha1,sha256,sha512 encryption. But the problem is now i have passwords in encrypted form which is
md5("password"+"salt")
At that point i didn't knew the password of users.So what i did
sha1(md5("password"+"salt"))
Now after few time in this field i found sha1 is also not too secure and is broken what should i do is use bcrypt() to make password secure .
so from now i will be using
crypt(sha1(md5("password"+"salt")))
Password is now very secure but the main problem still is about the time it will use to create hash value will always be greater than to use bcrypt("password")
Now what i want to say suppose if bcrypt is hacked and is found to be broken and in future there comes new cryptographic function that is more secure and. Than this way creating a password from old values will always be time consuming.
What could be the solution for this. as i know mailing users to change password is not always 100% successful. Another thing is to add a new field in database that store new hashed values and if all the fields are filled than remove the md5 values from db .But thing in this way previous hashed values are still visible.
So will this thing be going on, or you guys have some solution. :)
Upvotes: 6
Views: 1592
Reputation: 18840
So you have to update all the passwords of your users in database? If you twit your login script you will have to do nothing at all. Have a look at this:
Updating Md5 password hash to BCRYPT hash::
$passwordFromDatabase = "0d107d09f5bbe40cade3de5c71e9e9b7"; // md5 hash of "letmein"
$passwordFromForm = $_POST['password']; // $_POST['password'] == "letmein"
if(password_needs_rehash($passwordFromDatabase, PASSWORD_BCRYPT, ["cost" => 12]) && md5($passwordFromForm) === $passwordFromDatabase){
// generate new password
$newPasswordHash = password_hash($passwordFromForm, PASSWORD_BCRYPT, ["cost" => 12]);
// update hash from database - replace old hash $passwordFromDatabase with new hash $newPasswordHash
// after update login user
if(password_veryfi($passwordFromForm, $newPasswordHash)){
// user has logged in successfully and hash was updated
// redirect to user area
}else{
// ups something went wrong Exception
}
}else{
if($password_veryfi($passwordFromForm, $passwordFromDatabase)){
// user password hash from database is already BCRYPTed no need to rehash
// user has logged in successfully
// redirect to user area
}else{
// wrong password
// no access granted - stay where you are
}
}
The example above is universal. Instead of
... && md5($passwordFromForm) === ...){
you can use whatever nested hashing combination you have done to stored passwords. On the end it will end up as a BCRYP hash anyway. Below read more about encryption and security, and how to define the right value of cost parameter to hash user password.
SLOW ALGOTITHM
The current standard is to use a slow hashing algorithm. PBKDF2, bcrypt or scrypt all take both a password and a salt as input and a configurable work factor - set this work factor as high as your users just accept on login time with your server's hardware. Reference
GOOD PASSWORD
The length of your password should not be less then 8 characters and it should use at least one:
Setting up password 8 char long with lower and upper case and special char is capable of creating: 6 634 204 312 890 625 combinations. However if your password will be week let say 6 char small letters only you will only get: 308,915,776 combination. To make your account secure it is recommended to use more then 12 char for password length. CLICK for Password Combination Count Simulator
CRACKING SPEED (Changes every year giving crackers more process power of their GPUs or more powerful cloud computing)
When you design your password think about future processing power increase and tools that hackers will get.
This program IGHASHGPU v0.90 asserts to be able to do about 1300 millions of SHA-1 hashes (i.e. more than 2^30) in each second on a single ATI HD5870 GPU.
Assume a password of 40 bits of entropy, this needs 2^10 seconds, which is about 17 minutes.
A password of 44 bits of entropy (like the one in the famous XKCD comic) takes 68 minutes (worst case, average case is half of this).
Running on multiple GPUs in parallel speeds this up proportionally.
So, brute-forcing with fast hashes is a real danger, not a theoretical one. And many passwords have a much lower entropy, making brute-forcing even faster. Reference
SOLUTION
You can customise the speed of your algorithm by manipulating it cost. The higher the cost the longer will take to code and encode your password. The best would be probably aim for about 500 mls, wich make it really hard for attackers to brute force our password.
Password with 12 char and longer + slower algorithm will guarantee the decent amount of combination to be brute forced before the password will be cracked. Once we have decent password we can make life much harder to someone wanting to get in to our system by slowing the process of password verification to something that will make it really difficult and time/resource consuming. Set cost to a number that will affect in around 0.5s time taken to verify user password.
CUSTOMISE COST VALUE
How do you know how high should you set your cost since the script execution will be different for each server based on processing power and trafic ?
Well you should to measure the time it takes for verification process and customise the cost as it is appropriate for you.
<?php
/**
* This code will benchmark your server to determine how high of a cost you can
* afford. You want to set the highest cost that you can without slowing down
* you server too much. 8-10 is a good baseline, and more is good if your servers
* are fast enough. The code below aims for ≤ 50 milliseconds stretching time,
* which is a good baseline for systems handling interactive logins.
*/
$timeTarget = 0.50; // 500 milliseconds
$cost = 8; //start to measure from cost = 8
do {
$cost++;
$start = microtime(true);
password_hash("Ajd_hsk-K87&", PASSWORD_BCRYPT, ["cost" => $cost]);
$end = microtime(true);
} while (($end - $start) < $timeTarget);
echo "Appropriate Cost Found: " . $cost . "\n";
?>
The above function will return X number of cost that we need to use to meet our safety requirements.
Appropriate Cost Found: 13 //this result will be different based on your server machine.
This script is taken from php manual and enhanced to process 10x times longer. This would be generally secure way in most cases, but for admin and super admin login I would consider to make it even more time consuming (something about 1s), as those places are of more interest for real hackers.
Upvotes: 2
Reputation: 50328
Actually, MD5, if used properly, is still considered perfectly safe for password hashing. While there do exist practical collision attacks against MD5, which render it insecure for things like digital signatures, breaking a password hash would require a preimage attack, and all currently known such attacks against MD5 are purely theoretical.
(That said, to paraphrase Bruce Schneier, "attacks only ever get better", so starting to move from MD5 to more trustworthy hash functions, such as SHA-2 or SHA-3, is certainly not a bad idea even if you don't need to do it yet.)
The catch is that MD5 alone is unsuitable for hashing passwords for two reasons, both of which are actually deliberate design features (and shared by other hash functions like SHA-2 and SHA-3):
MD5 is deterministic, meaning that hashing the same input with MD5 always produces the same output.
This is a problem for password hashing, since somebody could (and, indeed, some people have) just compile a huge database of the MD5 hashes of common (and not so common) passwords, allowing anyone who knows the plain MD5 hash of any password found in those databases to just look it up and find the original password.
The solution is simple, and you already know it: combine the password with a random salt before hashing it, and include the salt as part of the final hash so that it can be used to verify the password later. With sufficiently many possible salts (say, a few billion, at least) to randomly choose from, compiling a hash database becomes impossible, since any single password could hash to billions of different values. Conveniently, this also means that, even if you happen to have two users with the same password, it's impossible to tell that just by looking at the hashes.
MD5 is fast. Normally this is considered a good thing, but in password hashing, it turns out that making the process too fast mostly just helps the attacker: a legitimate user doesn't really care if hashing their password takes 10 nanoseconds or 10 milliseconds, whereas an attacker trying to guess the password by hashing millions of passwords by brute force will appreciate every fraction of a nanosecond shaved off each hash calculation.
Again, the solution is simple and well known: just re-hash the password a few thousand (or more) times to slow down the calculation. The are even standardized ways of doing this, such as the PBKDF2 method. Alternatively, it's also possible to use a special purpose-built password hashing function like bcrypt or scrypt, which typically come with salting and adjustable iteration counts built-in.
Anyway... the point of all this, is that, in fact, calculating your password hashes as e.g.
hash = salt + bcrypt( sha1( md5( password + salt ) ) )
is perfectly fine, even if somewhat convoluted. Also, with that chain of hashes, almost all of the time is consumed by bcrypt, since it's the only one of the three hash functions deliberately designed to be slow. Thus, there should be no noticeable speed difference whatsoever between that chain of hashes and just bcrypt itself — and, in any case, you want the password hashing to be as slow as practically feasible.
Upvotes: 6
Reputation: 219804
PHP 5.5 introduces the Password API which addresses this issue:
The new Secure Password Hashing API in PHP 5.5
The RFC for a new simple to use password hashing API has just been accepted for PHP 5.5. As the RFC itself is rather technical and most of the sample codes are something you should not use, I want to give a very quick overview of the new API:
Why do we need a new API?
Everybody knows that you should be hashing their passwords using bcrypt, but still a surprising number of developers uses insecure md5 or sha1 hashes (just look at the recent password leaks). One of the reasons for this is that the crypt() API is ridiculously hard to use and very prone to programming mistakes.
By adding a new, very simple to use API we hope to move more developers towards bcrypt.
How to hash passwords
Creating password hashes can't be any simpler than this:
$hash = password_hash($password, PASSWORD_DEFAULT);
This will create a password hash using the default algorithm (currently bcrypt), the default load factor (currently 10) and an automatically generated salt. The used algorithm and salt will also be part of the resulting hash, so you don't need to worry about them at all ;)
If you don't want to stick with the defaults (which might change in the future), you can also provide algorithm and load factor yourself:
$hash = password_hash($password, PASSWORD_BCRYPT, ['cost' => 12]);
Verifying passwords
Verifying passwords is just as easy:
<?php // $password from user, $hash from database if (password_verify($password, $hash)) { // password valid! } else { // wrong password :( }
Remember: The salt and algorithm are part of the hash, so you don't need to provide them separately.
Rehashing passwords
As time goes by you might want to change the password hashing algorithm or load factor, or PHP may change the defaults to be more secure. In this case new accounts should be created using the new options and existing passwords rehashed on login (you can do this only on login because you need the original password to do a rehash).
Doing this is also very simple:
<?php function password_verify_with_rehash($password, $hash) { if (!password_verify($password, $hash)) { return false; } if (password_needs_rehash($hash, PASSWORD_DEFAULT)) { $hash = password_hash($password, PASSWORD_DEFAULT); // update hash in database } return true; }
The above snippet will keep your hashes up to date with the PHP default. But once again you can also specify custom options, e.g.
password_needs_rehash($hash, PASSWORD_BCRYPT, ['cost' => 12'])
.Compatibility layer for older PHP versions
The new API will only be introduced in PHP 5.5, but you can already use a PHP implementation of the same API now! The compatibility implementation will automatically disable itself once you upgrade to 5.5.
Upvotes: 8