tags:

views:

1131

answers:

11

I have a LAMP (PHP) website which is becoming popular.

I played it safe by storing the user passwords as md5 hashes.

But I now see that's not secure; I should have salted the md5 hash - because it's currently possible to decode unsalted md5 hashes using rainbow tables.

What can I do?

I don't want to make everyone type a new password.

A: 

sadly, your only way is to tell your users to renew their passwords.

you could also generate random passwords, but that is the same hassle.

edit

you could just double encode your stored passwords. so your new salted hashing algorithm would be:

md5(md5($new_password).$salt).':'.$salt

to update your old passwords use

md5($old_password.$salt).':'.$salt

to check if a provided password is correct simply use

list($stored_password, $salt) = explode(':', $salted_password);
if(md5(md5($provided_password).$salt) == $stored_password) {
  // you are now logged in
}
knittl
Although stretching with MD5 wouldn't probably give you a whole lot better security. You might want to try salting the MD5 hashes, and hash those strings using a more complicated hash.
Martijn
A: 

Two options here

  • Decode the passwords yourself, and re-encode them with a salt (I recommend something a little more fancy than MD5). You should inform the users that you're viewing their passwords unencrypted. It'll probably take a lot of time as well.
  • Make them retype their passwords, and store those salted and encrypted.

As far as I can see, there is no other way of recovering the passwords.

EDIT: Although MD5 is a hash and should not be decodable, it can be broken using rainbow tables: with probability almost one, you can find a unique (here's the probability) string of at most, say, 20 characters with a given hash, especially if your character set is limited, say, to alphanumeric. Strictly speaking, this is not decoding. For all practical purposes, it is. Extra note: producing the rainbow tables, and looking up 1000 password is still going to take a lot of time.

Martijn
md5 is a hash, it cannot be decoded.
Kobi
You cannot just "decode the password yourself".
Sam152
Actually, if you couldn't, then the original problem wouldn't exist. Fact is that anybody can, using rainbow tables. So the legitimate server admin can, too.
MSalters
No, you can decode an MD5 hash back to a value that would result into the hashed value. But that value doesn't need to be the original one...
Workshop Alex
+15  A: 

You can do a "2 step hashing" instead of creating a hash in a single step.

You could append each password hash to the username, and then hash it again. This will create an undecryptable hash thats salted with unique informations.

The usual process of salting is

salt+PWD -> hash

You could do something like: PWD -> Hash -> UserID+Hash -> Hash

(Note the UserID was only picked so a unique salt for each double hash exists... Feel free to make your salt more complex)

Heiko Hatzfeld
+1 - probably better than what I suggested, provided usernames never change.
Dominic Rodger
You mean a two step hashing.
Gumbo
Yes, sorry for the typo
Heiko Hatzfeld
This is the best solution! Maybe even the only solution, although it is annoying to do a double hash...
Workshop Alex
You only have to do it once, and you can also update the existing userbase with a single update.
Heiko Hatzfeld
Salts are better the longer they are. With userIds starting at 1, you get limited benefit from salting, depending on how eager the attacker is. Could be used as temporary measure until the user reauths and the password can be properly salted, and/or the userId 1 can be formatted as sprintf('%097d', $userId).
OIS
NB: this method will not work if someone already has a copy of your user table with usernames and presalted hashed passwords.
OIS
There are many ways to strengthen this. You could generate a random number, save it in the row and use it as part of the salt. You could hash the output of the hash repeatedly, to make it more expensive to generate tables. The list is endless.
Steven Sudit
Yup... Thats why i already mentioned this in my post "(Note the UserID was only picked so a unique salt for each double hash exists... Feel free to make your salt more complex)"But still... You have a SALT that changes for each user, so it does require a longer table. The moment the attacker gains access to your logic to store the passwords, he can reverse the process. (Assuning if he is able to get the table, he is also able to get the logic) The only thing you can do is to make it more expensice to create the rainbow table
Heiko Hatzfeld
P.s.: Its easier to get the passwords through user stupidity, since stupid users are highly available...
Heiko Hatzfeld
+5  A: 

Why not add a new column new_pwd to your user table, which stores the result of md5($originallyHashOfPwd . $salt). You can then precompute new_pwd and once that's done adjust your login checking to compare the result of md5(md5($entered_pwd) . $salt) to what's in new_pwd. Once you're done switching your login checking, delete the old column.

That should stop rainbow-table style attacks.

Dominic Rodger
The present column still has the old password -- which means if someone gains access to the database, they have the passwords.
George Stocker
Then delete the present column when you're done!
Dominic Rodger
+7  A: 

You can salt them on the fly. Add a piece of code so that, when someone logs in, it does the normal process (computes the MD5 sum of the password and checks it against the stored hash) and if that succeeds, recompute a salted version of the hash from the clear-text password they entered, and store it in the password file.

The only wrinkle is that you'll need to add an indicator for whether each MD5 is salted or not, since you'll have a mix of both for a while. Or, for a minor loss of security, you can check each password salted and unsalted and if either one hits, accept the login. Of course, if you detect that it was unsalted, then you upgrade at that point.

jbourque
+1 - this will leave the window open for rainbow-table attacks for a time, but allow you to close it in future, without overly complicating the login process in the log run.
Dominic Rodger
You could add an extra field to the table for "salted hash" - then check whether this field has been filled out. The only problem as I see it is you will have some users who don't come back to the site for a long time - maybe once you've converted the majority of users, simply add a mechanism for them to reset their password (via email).
DisgruntledGoat
A: 

Dynamically re-encrypt the passwords when the users log in the next time, i.e. first check whether it’s correct, afterwards encrypt it with a salt and store it again.

Bombe
+3  A: 

You can still use a salt. Just calculate another hash from the current hash together with a salt:

$newHash = md5($salt.$oldHash);

For new passwords you then need to use:

$hash = md5($salt.md5($password));
Gumbo
This is the way how they do it in VBulletin, btw ($hash = md5($salt.md5($password));)
FractalizeR
A: 

You can migrate the passwords by adding a column in your tables to store the new format.

When a user logs in successfully, if the new column is empty, put the stronger password in there and empty out the original column. If the new column has an entry, compare the input to the value in there.

Jon Seigel
+4  A: 

The answer is simple, make sure the keep a record or some sort of flag of which users have passwords on the new system of hashing, when they next login, authenticate them, calculate the new hash, flip the flag.

Now whenever someone logs in and the flag is set, authenticate them with the new hash.

Sam152
Good idea. Naturally, you'd want to delete the old hash value, too.
Martijn
+1  A: 

A great way to update the passwords while also making them more secure is to change to using a salted SHA1 for passwords. A SHA1 is harder to create a collision against, and it also has a different string length to MD5. A MD5 is 32 characters long, while a SHA1 is 40 characters long.

To convert these in PHP, you first check the string length of the stored password. If it is 32 characters long, check the password using your old method and afterwards, write a new one using SHA1 to the database.

If I remember correctly, this is precisely how WordPress handled this issue.

Ryan McCue
A: 

Salt the original hash as mentioned by others. Just a few pointers here:

  • Salts are better the longer they are. Also if they contain more then just [a-z0-9] but length is better first of all.
  • If someone already has a copy of your DB and you rehash the same passwords with salt, the rehash the old hash with salt will not work. Instead you really should force users to make a new password.
  • You should match new passwords (and passwords to be salted) up against various lists of the most commonly used passwords. These are used in "brute force" attacks. Prompt/force the user to change the password.
OIS