I have been reading up on password hashing, but all the forums I read are full of posts from people debating theory behind it that I don't really understand.
I have an old (and presumably extremely weak) password script that reads like this: $hash = sha1($pass1);
function createSalt()
{
$string = md5(uniqid(rand(), true));
return substr($string, 0, 3);
}
$salt = createSalt();
$hash = sha1($salt . $hash);
If I understand correctly, the longer the salt, the larger the table the hacker has to generate in order to break the hash. Please correct me if I am wrong.
I am looking to write a new script that is more secure, and I am thinking that something like this would be okay:
function createSalt()
{
$string = hash('sha256', uniqid(rand(), true));
return $string;
}
$hash = hash('sha256', $password);
$salt = createSalt();
$secret_server_hash = 'ac1d81c5f99fdfc6758f21010be4c673878079fdc8f144394030687374f185ad';
$salt2 = hash('sha256', $salt);
$hash = $salt2 . $hash . $secret_server_hash;
$hash = hash('sha512', $hash );
Is this more secure? Does this have a noticeable amount of overhead?
Most importantly, is there some better way to make sure that the passwords in my database cannot be (realistically) recovered by cryptanalysis, thus ensuring that the only way security will be compromised is through my own error in coding?
EDIT:
Upon reading all of your answers and further reasearching, I have decided to go ahead and implement the bcrypt method of protecting my passwords. That being said, for curiosity's sake, if I were to take my above code and put a loop on it for say, 100,000 iterations, would that accomplish something similar to the strength/security of bcrypt?
Salts can only help you so far. If the hashing algorithm you use is so fast that there is little to no cost for generating rainbow tables, your security is still compromised.
A few pointers:
Instead of deploying your own (inherently with flaws) hash/salt algorithm, why not use one that was developed by security professionals?
Use bcrypt. It's been developed exactly for this in mind. It slowness and multiple rounds ensures that an attacker must deploy massive funds and hardware to be able to crack your passwords. Add to that per-password salts (bcrypt REQUIRES salts) and you can be sure that an attack is virtually unfeasible without either ludicrous amount of funds or hardware.
The Portable PHP Hashing Framework in non-portable mode allows you to generate hashes using bcrypt easily.
You can also use
crypt()
function to generate bcrypt hashes of input strings. If you go down that route, make sure you generate one salt per hash.This class can automatically generate salts and verify existing hashes against an input.
You may use this code as such:
There's really no need to try to implement your own series of hashes. Here's a simple class that implements bcrypt:
Using:
The upside of bcrypt is that you can make it computationally expensive to hash a password (via
$cost_factor
). This makes it impractical to try to recover an entire databases' passwords by brute force.About Salt Values
Yes, that's correct. Although if someone tries to break a hash of only one user, salt values are useless. Salts are useful for preventing (slowing down) attackers doing dictionary attack on all of your users hash values.
Let me explain that with an example. Suppose you have 3 users in your system and you don't use a salt value, so your database would like this:
Now let's assume that an attacker achieves to get a copy of your database. He could now do a dictionary attack by doing:
And so, he could check if one of the 3 users has the password
possible_password
by only calling the hash function one time.No suppose you save the hash values which were combined with salt values in your database like this:
And again an attacker copies your database. But now in order to see if
possible_password
is used by one of the 3 users he must do the following checks:As you see, in this case the attacker is slowed down by a factor of 3 (the number of users in your system), as he must hash 3 diffferent strings. That's the general idea behind salt values, you could read more on wikipedia.
But in your case, the salt is too big. What you want to prevent is 2 different user hashes to have the same salt value. So, for example a salt of 2 bits length won't probably be a good idea (for more than 4 users it will be sure that 2 have the same salt value). Anyway, a salt value of more than 48 bits will be enough.
Also, there is not really a point in hashing the salt here
$salt2 = hash('sha256', $salt);
, this could slow things somehow, but in general adding more complexity in your system is considered bad when dealing with security.General
Finally, it's never good to have specific values in your code when dealing with security, like
$secret_server_hash
, such constant values should always be avoided.It's better that you use SHA-2, instead of MD5, because in the recent years some security vulnerabilities have been found in MD5 (although they are not yet really practical).
So I would do something like this:
And then save the
$hash
at your database.Anyway, as some users already pointed out. Instead of creating your own security functions (which is a good way for learning about security) you should better use well-known libraries which are tested by a greater amount of people and hence probably more secure. In your case you should have a look at
crypt
which does what you need.