简体   繁体   中英

Salting passwords PHP, MySQL

Is the following a good way to salt passwords?

hash('sha256', $_POST['password'], $_POST['email'])

I am using the user email as a salt. Some people do not use emails, some others say to use a random number.

Even if I use a random number then I will still need to store it on my MySQL table, so the salt will still be known anyway, and with the added benefit of using emails is that the possibility of rainbow tables is greatly decreased, even if I was to use a 16-bit integer?

The idea behind a salt is to prevent a hacker from using a rainbow table. For instance, if the hacker is able to compromise your database and figure out what the hashed password is he can't easily reverse engineer the hash to find a value that would generate the same hash.

However, there exist tables of already hashed words called rainbow tables. Some people have already gone through the trouble of calculating the hash of every word in the dictionary and other common passwords. If the hacker has one of these tables, plus the hashed password from your database, it makes it very easy to figure out what the password is.

However, a salt changes all that because now, instead of hashing the password, you are hashing the password plus some random value which means that the rainbow table is now useless. It does not matter if the hacker can compromise the salt.

It is perfectly fine to save the salt in clear text. You want to use something that is not uniform across all users either because, again, that defeats the purpose. I personally like to use the timestamp the account was created.

Does that make sense?

What happens if a user changes his email address? You won't be able to verify his/her password anymore because the salt value will be gone.

You shouldn't use anything as a salt that is likely to change over time. Generate a random salt (long enough to defeat rainbow tables) and use it together with the password to generate the hash.

Right now the best possible solution to use in PHP for password hashing is to use the bcrypt (blowfish) implementation. Why? There are several reasons:

  • variable 'work' parameter
  • built-in salt

Keep in mind that if you are not running php 5.3, then crypt_blowfish may not be available on your system.

Work Parameter

Blowfish/crypt is already has an expensive setup time but by setting the work factor you can increase the amount of time it takes to calculate a hash. In addition, you could easily change that work factor in the future as computers get faster and are able to compute hashes more easily. This makes the particular hashing method scale.

Built-in Salt

For me this is just laziness but I like that the salt & pass are stored together.

Implementation

To use blowfish you'd create a hash as follows

// salts must be 22 characters
$salt = "ejv8f0w34903mfsklviwos";

// work factor: 04-31 (string), each increase doubles the processing time.
// 12 takes my current home computer about .3 sec to hash a short string
$work = '12';

// $2a$ tells php to use blowfish
// you end up with a string like '$2a$12$mysalthere22charslong'
$options = '$2a$' . $work . '$' . $salt;

$hashedPass = crypt($plaintext, $options);

To verify a hashed password is simplicity:

if(crypt($user_input, $stored_password) == $stored_password) { echo "valid!"; }

Now, if at any given time you want to increase the work factor you could take the submitted pass after a successfull login, and rehash and save it. Because the work factor is saved along with the salt & password, the change is transparent to the rest of the system.

Edit

There seems to be some confusion in the comments about blowfish being a two way encryption cypher. It is not implemented as such in crypt. bcrypt is an adaptive password hashing algorithm which uses the Blowfish keying schedule, not a symmetric encryption algorithm.

you can read all about it here: http://www.usenix.org/events/usenix99/provos.html

or you can read even more about using bcrypt (the hashing implementation of blowfish) here: http://codahale.com/how-to-safely-store-a-password/

i suggest using iteration such as below. The crypt could be replaced with md5 or any other hashing algorithm. the 10 could be any number.

$pass=mysql_real_escape_string($_POST['pass']);

$iterations = 10;
$hash = crypt($pass,$salt);
for ($i = 0; $i < $iterations; ++$i)
{
  $password = crypt($hash . $pass,$salt);
}

In addition, you could add any other variable. I hope this solve the problem

You could use this:

$salt='whatever';
$a=hash('sha256', $_POST['password'], $salt);
$b=hash('sha256', $_POST['email'], $salt);
$hash=$a.'-'.$b;

When the user changes the email, just do:

$old_a=substr($old_hash,0,strpos($old_hash,'-'));
$new_b=hash('sha256', $_POST['email'], $salt);
$new_hash=$old_a.'-'.$new_b;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM