2

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?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Basic
  • 1,818
  • 5
  • 21
  • 31
  • 1
    Also note that this will not work as you expect. The third parameter to `hash()` is a boolean indicating raw output. Either append the email to the raw password using `.`, or change the function name to `hash_hmac` (which is what I would suggest)... – ircmaxell Apr 17 '11 at 22:53
  • What do you mean bud? I don't understand that at all lol :p – Basic Apr 17 '11 at 23:31
  • 1
    You're passing the salt as the third parameter to `hash`. That parameter is not for salts. Instead, the `hash_hmac` function can be used (which is designed for combining two strings in a secure way while hashing). To see what I mean, try comparing the output of `hash('sha256', 'foo', 'bar');` and `hash('sha256', 'foo')`. They should be the same. – ircmaxell Apr 17 '11 at 23:51

5 Answers5

8

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?

Jared Farrish
  • 48,585
  • 17
  • 95
  • 104
Chris Thompson
  • 35,167
  • 12
  • 80
  • 109
  • Yes thanks for this, so if I salt it with a timestamp, save the timestamp in the database, then compare? – Basic Apr 17 '11 at 21:58
  • I've done this. I hash the password with a preset (large) value, then again with the timestamp of the account's creation. – Winfield Trail Apr 17 '11 at 22:07
  • @Chris - Big paragraphs give me migraines. :) – Jared Farrish Apr 17 '11 at 22:07
  • @Jared Farrish, sorry, I should have had a disclaimer! Thanks for fixing that :) – Chris Thompson Apr 17 '11 at 22:08
  • @Kerin, while that's fine, you don't really gain anything from the preset value. The security comes from the timestamp – Chris Thompson Apr 17 '11 at 22:09
  • @Basic, Yep! That's how you'd do it! – Chris Thompson Apr 17 '11 at 22:09
  • Hmm, I am stuck between, sha256 and a timestamp and bcrypt now, anyone have anything else to add between the two, I think using @Chris Thompson's method will be just as safe as bcrypt. – Basic Apr 17 '11 at 22:11
  • 1
    @Basic, you want to use sha256. You gain nothing from using an encryption algorithm that can be decrypted. In fact, you introduce weakness with no gain because if somebody compromises your key, they can access all of your passwords. However, sha256 ensures that even if your database is compromised, user's passwords won't be exposed. – Chris Thompson Apr 17 '11 at 22:17
  • I don't see a reason for not using a single salt. – Christian Apr 17 '11 at 22:17
  • 2
    @Christian because the attacker could simply recreate the rainbow table using that salt. It would be time consuming, but not impossible. Using a different salt would require a different rainbow table for each hash which would be very difficult and incredibly more time consuming for the attacker to compromise every password in the database. – Chris Thompson Apr 17 '11 at 22:20
  • 2
    Make sure you BACK YOUR DATABASE UP regularly and do verification and validity checks on the backed up data on a regular basis (don't just backup and forget it). If you use a db-stored salt like the signup date, which is a great strategy, just make sure you don't lose it for all your users. :) – Jared Farrish Apr 17 '11 at 22:23
  • sha256 and timestamp it is, thanks chris thompson, big help :) – Basic Apr 17 '11 at 22:23
  • I don't see this possible at all. The only known rainbow table that "works" is the one where no salting was used. Recreating the rainbow table is the same as brute forcing the password - if someone has this much power of creating a rainbow table, you're in a huge problem already. – Christian Apr 17 '11 at 22:23
  • What would I have to set the length of my mysql row? What is the max number of characters that can output? Thanks again. – Basic Apr 17 '11 at 22:24
  • @Jared, oh man, just thinking about that gives _me_ a migraine! – Chris Thompson Apr 17 '11 at 22:24
  • @Christian - If you're not enforcing strong passwords, you could possibly only select the top 100 or 200 favorite passwords and have a huge problem on your hands. Especially if your admins using something like `abcImGod`. – Jared Farrish Apr 17 '11 at 22:26
  • @Christian, not exactly. The limitation on rainbow tables is memory, not CPU power. See http://en.wikipedia.org/wiki/Rainbow_table for more. – Chris Thompson Apr 17 '11 at 22:28
  • @Jarred Farrish - If someone's using such a password, you're quite fked up in either case, no matter how long your hash is, nor how long it takes doing the hash. – Christian Apr 17 '11 at 22:29
  • @Chris Thompson - You're *generating* a rainbow table, not *using one*. As such, this is as slow as brute forcing the password in the first place. – Christian Apr 17 '11 at 22:32
  • @Christian - Depends on your users and the barriers to uptake you want to put in place. Banking site? Hello Kitty fan forum? Lots of people do stupid things thinking it's a great setup. I don't see anything wrong with what Chris is suggesting, it's a good middle ground IMO. I see your point (and have done the same thing) on using a single salt, but the rainbow table issue can be mitigated rather easily with a strategy like Chris'. – Jared Farrish Apr 17 '11 at 22:34
  • @Christian, alright think of it this way. Which would take longer: generating 1 rainbow table or generating a rainbow table for every password in the database? – Chris Thompson Apr 17 '11 at 22:35
  • @Jared Farrish - I didn't say he's wrong, I already said he's right. I'm just saying that his idea of timestamp hashing is way over the line in this case. If you're expecting someone to brute force your hashed passwords, be it one or the whole DB, it means you have the entire setup wrong. Again, this isn't about the hash length or time taken, but the whole setup. – Christian Apr 17 '11 at 22:36
  • @Christian - How much more difficult is it? You're suggesting a least is best option as being most appropriate, but I think Chris' suggesting has merit in the sense that the user is ultimately not impacted. Overdoing security that's transparent to the user is not a bad thing, right? – Jared Farrish Apr 17 '11 at 22:39
  • @Jared Farrish - My point is that this isn't overdoing security, but merely over-engineering. If someone's able (and will bother) to retrieve your password with a single hash, chance is s/he's able to do this over all of your DB - in which point, you'd probably want you had a different security setup than just salting passwords. – Christian Apr 17 '11 at 22:43
  • @Christian - That's true, if someone has your database, you're screwed. I can see what you're saying. The only thing I'd worry about, though, is how brittle it is to rely on a single salt vs a db-stored salt. But you have a valid point (as far as where you apply your effort). – Jared Farrish Apr 17 '11 at 22:48
  • I disagree with the timestamp salt. At first it appears that there is 4 bytes of entropy (32 bit int). However we know that the most part of that window is invalid (since the registration can't be in the future, and we know roughly when you site went live. So in practice you'll maybe have 2 bytes at most of entropy. Instead, I'd suggest using at least a 8 byte randomly generated salt for each user. If you're using a sha256 hash, I'd recommend using a 512 bit (64 byte) salt since that's the internal block sized used by the algorithm... – ircmaxell Apr 17 '11 at 23:11
  • @ircmaxell, you are correct, there was a question about timestamp as salt not too long ago: http://stackoverflow.com/questions/4983915/is-time-a-good-salt/4984044#4984044 – Jacco Apr 18 '11 at 08:02
4

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.

halfdan
  • 33,545
  • 8
  • 78
  • 87
  • If a user changes his email then when I insert the new email, I just do the same as on account creation. – Basic Apr 17 '11 at 21:36
  • 4
    How? Do you store the plain text password to recalculate the hash? – halfdan Apr 17 '11 at 21:37
  • 1
    Exactly right about the user changing her email address locking her out. However, using a systemwide salt is less resilient than a per-user salt. With (only) a system-wide salt, I only need to compute one rainbow table. Better to use a per-user random salt, and force me to brute-force passwords one at a time. – timdev Apr 17 '11 at 21:39
  • Hmm, didn't think of that, it will not be possible to do it then. Thanks for your input, would you advise using a nonce instead and storing that in the database? I have read that you should use a user specific salt per account rather than just one salt for all. – Basic Apr 17 '11 at 21:39
  • 1
    Don't EVER define a salt and use it across the entire application - it defeats the purpose of having a salt. A salt needs to be unique per password. – Erik Apr 17 '11 at 21:44
  • @Erik, @timdev, yeah you guys are right. Adjusted the answer accordingly. – halfdan Apr 17 '11 at 21:47
  • I'd just add that we use usernames - to ensure against issues when a username change, we store the username used at password creation in a `Salt` field in the user table. This way, it's unique, persistent and 100% flexible i we decide to salt differently in future – Basic Apr 17 '11 at 21:59
  • @Erik, what the heck are you saying? A salt is there to prevent the use of rainbow tables. ANY salt does this. The only reason stuff like wordpress forces you to use a new salt is so that hackers don't just create a rainbow table for the wordpress salt. You don't have to change the salt per password, at all. – Christian Apr 17 '11 at 22:05
  • Thanks for all your comments great help :) – Basic Apr 17 '11 at 22:06
  • @Christian If you use a single salt I can generate one rainbow table to attack your entire database. A per-password salt means generating a new rainbow table for each password. – Erik Apr 17 '11 at 22:28
  • As I said before, and I will again, that argument has several flows, which this time I won't even bother arguing on. – Christian Apr 17 '11 at 22:34
1

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/

Erik
  • 20,526
  • 8
  • 45
  • 76
  • Everyone is saying use bcrpyt, I think I will as you can change the $work if it is causing overload and vice versa :) Thanks – Basic Apr 17 '11 at 22:08
  • **Blowfish is a cypher, not a hashing algorithm. MD5 is a hashing algorithm.** – Christian Apr 17 '11 at 22:08
  • the password-hashing method used crypt uses an algorithm derived from Blowfish that makes use of the slow key schedule – Erik Apr 17 '11 at 22:10
  • Explaining myself above, the password with this method is entirely decipherable, and given access to your server, **hackers will do it**. Hashing algorithms like SHA1/256 and MD5 are **destructive** you can't recover the password. Rainbow tables simply match an md5 value with a known hashed password. If you use a salt, **any salt**, the rainbow table concept is entirely useless. – Christian Apr 17 '11 at 22:12
  • Christian would you use @Chris Thompson's method over bcrypt? – Basic Apr 17 '11 at 22:12
  • 1
    Christian you are incorrect. A password hashed with this method is destructive and can not be recovered by any means other then brute force. In fact this same method is used by OpenBSD – Erik Apr 17 '11 at 22:13
  • @Eric - Blowfish is a cipher, see here: http://en.wikipedia.org/wiki/Blowfish_%28cipher%29 If they did use it as a hash, I really don't see why anyone needs it to work as such. @Basic - Chris Thompson's the right description of how salting and hashing works. My implementation works with it nicely, so yes. – Christian Apr 17 '11 at 22:16
  • @Christian I edited the article, please read the links at the end. the bcrypt blowfish implementation is NOT encryption. Its currently the most secure hashing function available to the public and is likely to remain so for quite some time. It's also able to scale against faster and faster computers by changing the work factor. – Erik Apr 17 '11 at 22:21
  • @Erik - OK, try editing this one: http://www.schneier.com/blowfish.html For your convenience, here's an excerpt: *Blowfish is a symmetric block cipher that can be used as a drop-in **replacement for DES or IDEA**. It takes a variable-length key, from 32 bits to 448 bits, making it ideal for both domestic and exportable use. Blowfish was designed in 1993 by Bruce Schneier as a fast, free alternative **to existing encryption algorithms**. Since then it has been analyzed considerably, and it is slowly gaining acceptance as a **strong encryption algorithm**.* – Christian Apr 17 '11 at 22:26
  • 1
    @christian. You may want to read before you continue this argument. One last time: bbcrypt is an adaptive password hashing algorithm which uses the Blowfish keying schedule, not a symmetric encryption algorithm. Yes blowfish is a cypher. You are correct. Bcrypt is not. I refer to it as "blowfish" only because PHP calls it "CRYPT_BLOWFISH" in their docs. It is not blowfish the encryption algorithm – Erik Apr 17 '11 at 22:31
  • Erik is correct on this one. BCrypt hash is a blowfish based hashing algorithm. It makes use of the notoriously expensive key-setup routine in Blowfish cipher to make the derived hashing algorithm as-slow-as-needed. – Jacco Apr 18 '11 at 08:06
0

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

Guy Coder
  • 24,501
  • 8
  • 71
  • 136
-1

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;
Christian
  • 27,509
  • 17
  • 111
  • 155
  • 1
    Don't you just love it when those with a losing argument go on a downvote rampage? – Christian Apr 17 '11 at 22:33
  • Well, the 'whatever' salt is not random, so that at least is wrong. and there is no explanation about why you, in contradiction to the common practice, choose to add an email address to the hash. – Jacco Apr 18 '11 at 08:09
  • @Jacco - **[1]** I assumed that anyone that has half an idea of what salting is about would replace the salt, even if it weren't "whatever" but something random. Ideally, the salt shouldn't be known to the attacker. By the way, the user is more likely to replace "whatever" than something random I entered. Oh and by the way, "whatever" is a good enough salt. No one with a sane mind would be brute forcing salts. **[2]** Since the OP wanted to add an email to the hash, I did it for him in a usable manner. **[3]** Just because I don't follow cargo-cult programming, doesn't mean I'm wrong. – Christian Apr 18 '11 at 09:35
  • 1
    A salt is random by definition. If it is not random, we call it a key. And no, a constant value is NOT a good enough salt. A more extended writup on salts: http://stackoverflow.com/questions/1645161/salt-generation-and-open-source-software/1645190#1645190 Also, I don't say you are wrong for deviating from the common best practice, but you should at least explain why you do so. The reference to cargo-cult programming is not necessary, it implies that I do not know the 'why' behind the comment and more or less disqualifies any further discussion. – Jacco Apr 18 '11 at 10:23
  • @Jacco - Do I really have to go through my reasons again? **[1]** You lost the meaning of random in that context. Random means something not easily guessable by the attacker. Considering the length is small anyway, "whatever" doesn't make a good salt, but it's not "not a salt" either. **[2]** You should have bothered with reading the OP's question, rather than resorting to such an argument, should I be blamed if someone doesn't understand the context? – Christian Apr 18 '11 at 22:57