views:

474

answers:

6

I'm making an application in PHP and there is a requirement that it must be possible to decrypt the passwords in order to avoid problems in the future with switching user database to different system. Consider that it's not possible to modify this future system's password method and I need plain text passwords in order to have the passwords generated.

The plan is to encrypt the user's password with a public key that is stored on the server. Authentication is done by encrypting the input and comparing the results. There is NO decryption done. The private key capable of the decryption is stored off-site for later usage.

What encryption/decryption algorithm would you suggest? Are the encrypted passwords still as safe as hashing (MD5/SHA1) when you consider the private key is not available to the attacker?

A: 

Part of what you are proposing is a clear violation of CWE-257. If you need to change the password storage system, then you can update the password hash when the user logs in next. Passwords should never be encrypted and then stored, they should be stored as a hash. No, md5() and sha1() are very insecure message digest functions and collisions have been intentionally generated against these algorithms. They should never be used for passwords. The use of md5() for secuirty purposes is a recognized vulnerability CWE-327.

You should use any member of the Sha2 family, sha256 is a great choice. The mhash php library has all members of the sha2 family. There is currently a competition for sha3, and this should be finalized in 2012.

Yes, all passwords should be salted with a large random value. You want to keep the salt a secret from the attacker. For instance the salt should be stored on the local hard drive so that if an attacker had sql injection to obtain the password hash, they won't have the salt. This prevents an attacker from using John The Ripper with a dictionary, instead they will have to brute force the salt separately with a password that is known to the attacker. If each password has its own salt, then it will be extremely difficult to break the salted hash.

Hashing passwords is all about delaying the attacker as much as possible.

Rook
It's a pet peeve of mine when people say that MD5 shouldn't be used for passwords because of the collision-generation method that was discovered. That vulnerability has absolutely nothing to do with being able to break a password hash. You still shouldn't really use MD5 for passwords, but the reason is that MD5 is *too fast*, so it's easy to generate MD5 rainbow tables. It has nothing to do with the vulnerability that everyone always cites (and apparently never actually reads).
Chad Birch
Both MD5 and SHA-1 are still safe enough for most applications. Yes, it is possible to generate collisions, but that is possible for every hashing algorithm (by definition). Also those generated collisions you are talking about just made it possible to find *two* inputs that yield the same hash. Generating a second input to a already known hash however is still something that will take long enough to count as quite secure.
poke
Storing salts outside of your database would be incredibly slow (since you have to find the correct salt for each password). Salting is about breaking rainbow tables. It's possible to make things more annoying for an attacker beyond that, but it would make things more annoying for you as well.
Brendan Long
@Chad Birch Actually, a lot of effort is put into making message digest functions fast. A slow cryptographic primitive would never be approved by NIST. Also, the use of md5() is a clear violation of cwe-327 (http://cwe.mitre.org/data/definitions/327.html). Also you are incorrect, because md5 is broken it opens a short cut in the brute forcing process: (http://www.blackhat.com/presentations/bh-usa-09/BEVAND/BHUSA09-Bevand-MD5-PAPER.pdf). That is a killer blackhat talk if you can find the video.
Rook
@Brendan Long, Not having the salt makes the hash **A WHOLE LOT MORE DIFFICULT TO BREAK** .
Rook
You suggested to store the salts outside of the DB to prevent them from being read via SQL-Injection. It would be much more efficient to prevent the SQL-Injection in the first place which isn't too hard anymore. PDOs prepared statements pretty much do the trick. Or you could completely hide the DB behind an ORM layer.
Techpriester
@The Rook Again, you're just showing that you haven't actually understood the vulnerability. The title is "MD5 Chosen-Prefix Collisions on GPUs". Cracking a hash is not chosen-prefix.
Chad Birch
@Techpriester You are correct. But, secuirty is about layers. Having a secure hash, is required layer of secuirty.
Rook
@Chad Birch How many exploits have you written? Is it more than me: http://milw0rm.com/author/677 ? STFU.
Rook
@Chad, collisions in md5() and sha1() require a "chosen prefix" to exploit. This "Chosen Prefix" takes a very long time to generate. If the attacker cannot control the prefix, such as if there was a SALT in the way, then a collision cannot be generated. You need to read that paper.
Rook
@The Rook: You're right, if the attacker doesn't have the salt, they can't break the hash at all, but is it worth it? The speed difference would be VERY noticable, and you could just as easily fix your SQL injection problems.
Brendan Long
@The Rook: If you are able to get access to an application in the first place, you don't even need to care about passwords, you can just change them or something. This discussion is just about passwords and password encryption, and assuming an application is broken is just stupid. And btw. you still didn't understand what hashing collisions are about.
poke
@Brendan Long And that is up to the programmer, I am just describing a **very** secure method for storing passwords, and i am sacrificing resources to accomplish this.
Rook
@poke PHP's mysql_query() doesn't allow query stacking so an attacker will never be able to modify the password. You should try exploiting a sql injection vulnerability, or just look at one of my exploits :).
Rook
Come on, give me more -1's. What you guys can't take the truth?
Rook
`$login = "Robert'); DROP TABLE students; --"`
macek
@smotchkkiss try doing that with **mysql_query()** You are confused with M$-SQL exploitation because you have never written a sql injection exploit for PHP/MySQL.
Rook
@smotchkkiss Haha, good old Bobby Tables.
Rook
@Chad But salting passwords will prevent from use of rainbow tables. So what is the reason to not use MD5 for storing passwords? I personally use it.
Petr Peller
@The Rook I just can't stand the first 2 paragraphs. Storing passwords via sha1/md5 has IMO nothing to do with CWE-327. Salted passwords cannot be broken using rainbow tables and collisions have nothing to do with recovering original content from hash. Could you add more information about weakness of storing passwords hashed with md5?
Petr Peller
@poke the majority of your users use the same password for multiple applications. If someone gain access to your database (hint : the janitor), he can get a ton of login, emails + password. Try them on other applications (gmail, facebook) and enjoy.That's why your users' password should be treated as the most dangerous data in your system. And if you can, don't use passwords and let other services do the job of authenticating people for you.
Arkh
+2  A: 

Being able to decrypt the passwords is a bad idea (and there's probably not any way of doing it that would be much better than storing them unencrypted). It sounds like your main problem is being able to use the passwords if you change your storage method. Just do what Linux does, store how you're hashing the password with the password. So for example $1$salt$hash is MD5. That way, if you decide to change how passwords are stored, you can still check against the old passwords (and if someone logs in correctly, you can update their password with the new hash).

Brendan Long
This would require me to modify the future platform, which I rather not do. Granted it will require some effort to move the plain passwords there too, but at least it won't require any administrative effort after the one time move.
Jammer
Couldn't you use something that you know will be supported in the future? For example, SHA512 is easy to do on pretty much any platform (if you can find a library to do encryption, you can probably do sha512 too).
Brendan Long
I agree, you shouldn't sacrifice security just to make it easier to switch to the *next* encryption method. Just start with a good one in the beginning, and you don't need to care much about any future changes.
poke
A: 

For most applications it is more than sufficient to store SHA-1 hashes of passwords.

Yes, there are known collisions in most hashing algorithms, but that doesn't imply an actual attack vector. Especially when you're salting the hashes.

For your salt: Store it in a configuration file that is not accessible from the outside but can be read by your PHP installation.

Techpriester
-1 What you are proposing is a recognized vulnerability. I'll see you on BugTraq.
Rook
I'm looking for encryption solution, not hashing.
Jammer
Storing a single salt is only slightly better than not using one, using a salt for each password is a better idea.
Brendan Long
It all depends on how much security you actually need. As I said: For most applications, simple salted hashes will be sufficient. Many applications sadly are also so full with their own security issues that a less-than-perfect hash algorithm is the least of their problems .
Techpriester
+3  A: 

Don't decrypt the password. If you need to change the password system in the future, add a field called storage_type (or whatever).

Then, when you need to change the passwords, you will check if it's an old password. If it is, next time they login, you can change the password encoding. Otherwise, login with the new system.

Macha
+3  A: 

I'll rephrase Jammer's approach -

  1. Generate a public/private key pair. Hard-code the public key on your webserver. Store the private key in a physical bank locker, outside the reach of webserver/database/any developer.
  2. When user registers, encrypt password + salt using public key. This step is identical to using a hash algorithm. Store the encrypted password + salt in the database.
  3. When you want to verify the password, encrypt it again, and compare it to the value stored in the database.

If an attacker gets the database, he can't decrypt the passwords because he doesn't have the private key. He cannot get the private key because it is in a bank vault outside his reach. Two identical passwords will still be stored differently in the database because of the salt.

I don't recommend using the above approach because at any point of time in the future someone could abuse the private key and get access to all passwords.

But if you guarantee that the private key will always remain private, then I don't see a technical flaw.

I could be wrong, of course.

sri
Its also rather expensive in terms of processing compared with just hashing the password.
symcbean
@Sripathi Correct, but what algorithm should I use?
Jammer
@symcbean it might be more expensive, but I recon this won't be a problem. Plus any attacker will have to spend so much more time when performing dictionary attacks against the database.
Jammer
Indeed, the reason bcrypt is suggested for hashing passwords is because it is designed to be slow.
Douglas Leeder
@Jammer - you should read the discussion in this thread - http://stackoverflow.com/questions/2283937/how-should-i-ethically-approach-user-password-storage-for-later-plaintext-retriev. Its a long discussion, but very pertinent to your question.
sri
+1  A: 

The only problem I see is that most public-private key encryption code out there will encrypt a symmetric key using the public key, and rely on the private key decrypting that, then use the symmetric key to encrypt the message.

You want to use the public key to directly encrypt the password+salt.

So attacks against your system boil down to:

  1. Attacks against general public/private key encryption
  2. Attacks against your private key store.
Douglas Leeder