What are the best practices to encrypt passwords stored in MySql using PhP?
Solution 1
Use bcrypt
. If someone has the user table of your database, then they can use brute force/rainbow tables/etc to their heart's content. Even with salt, if you're using MD5 or some other fast-hashing algorithm (which aren't designed to solve this problem, by the way); it's just a matter of time before it can be cracked.
Any well-known and widely-supported hashing algorithm is going to have this same basic "flaw" (if you can call it that; it's really by definition). The difference is that bcrypt
is slow as molasses when performing the hashing operation, rendering a brute force attack much less effective.
For an absolutely great discussion on the merits of bcrypt
, the dangers of other approaches, and the difficulty of password security in general, read this thread. It has lots of comments by many people that are much more knowledgeable about this sort of thing than I am, and it should hopefully help you understand more of the issues at stake.
Solution 2
Assuming you're using username and password as authentication tokens you can safely store the following to ensure the data can't be compromised.
- Username (in plaintext)
- Salt (random string)
- Salted Hash (sha1(username + salt + password))
Using the scheme, an attacker cannot use rainbow tables against you and the passwords are not recoverable by any (reasonable) means. (That is, as long as your attacker isn't the government)
Even though the attacker has the salt and hash pairs it's not possible to use rainbow tables because all the possible hashes will need to be computed anyway, using the salt that they've been given, so it's a brand new brute force attack for each user.
Even with the source code and attacker won't be able to get hold of the passwords because the strength/security is in the hashing algorithm, not your code.
Combine this with using bcrypt as per Donut's answer and you're really quite safe. That is:
- Username (in plaintext)
- Salt (random string)
- Salted Hash (bcrypt(username + salt + password))
Solution 3
Taking advice from here, for added fun you can dynamically change your salt as well. For example, use different salts for usernames of different length, use the user's registration date as the salt. This makes it that even if someone DOES get to your database, they can't just re-generate the hash, they have to calculate a hash table for each salt that you used.
Solution 4
If your users are over the internet, OpenId would be one of your best options. http://openid.net/
If your users are on your network, can you do Integrated Security?
In other words.. do not store their passwords.
Solution 5
Usually "salted" passwords (like with bcrypt) mean that not the password itself is stored, but only something like
salt
hash(salt with password appended)
Now if the kiddie has your database (and of course, the code - there is no point in keeping the code secret), he/she can only guess passwords, calculate the salted hash, and compare. If the hash function is expensive (like bcrypt is), than guessing is expensive too.
Admin
Updated on July 26, 2022Comments
-
Admin almost 2 years
I am seeking advice on how to securely store passwords in MySQL using PHP.
Overlooking the limitations of PHP itself, I want to know more about salting, hashing, and encrypting these bad boys.
Obviously people will continue to use weak passwords unless forced to do otherwise, but it's how I am storing them that is important to me. My user's passwords are far more important to me than the database itself, and as such I want to keep them in such a way that it will be painstaking and monotonous for any script kiddie trying reverse. Obviously with due diligence just about anything can be defeated, but I wouldn't mind making this particularly bothersome.
There are two scenarios we are looking at.
- The kiddie has a complete copy of the database.
- The kiddie has a complete copy of the PHP used to craft the password, and the database.
Any and all advice on this topic is graciously appreciated.
-
eaj over 13 yearsYep, bcrypt does have the advantage that you can make the hashing process arbitrarily slow. No big deal on your server if it takes a quarter of a second to hash a password. But it matters to the kiddie if he can only compute four hashes per second instead of thousands.
-
Donut over 13 years@Time Machine Lol, I figured I'd get it out there while I was typing up the rest of my answer :)
-
NotMe over 13 years+1: With a caveat. "by any means".. not exactly. You can still build rainbow tables and find collisions. However, you'd need one for each salt. Which, although possible, is just not feasible (hence the +1). If you're the NSA or some other governmental agency then all bets are off.
-
Admin over 13 yearsAssuming they have the database/php they are well-aware of the salt. Brute forcing their way past this is simply a matter of adding the salt to the front of the password.
-
Admin over 13 yearsWon't prevent dictionary-attacks.
-
Admin over 13 yearsThey may be over the internet, and they may also be local at times. Because of this I can not depend on OpenID. Thanks though!
-
Bart van Heukelom over 13 years@MAtt: The salt must be known, otherwise passwords can't be verified. Brute force is theoretically possible, but not in practice. @Time Machine: How so? That's what the salt is for. If it's sufficiently long and unique, no dictionary will have it.
-
Admin over 13 yearsAnd if your really don't care about time, or your server is a supercomputer: simply set the work factor to 10000.
-
Nathan Garabedian over 13 yearsYou could do a dictionary attack against the PHP code because it already contains the code necessary to convert the dictionary password to a salted hash. In other words, if I had the database and PHP code, I would run a dictionary attack against the database using the code. The PHP code would use the salted hash to check each password against the database.
-
Nathan Garabedian over 13 yearsThis is an excellent addition to the topic. It was discussed in a security class I took. Actually all you need to do is increment the salt by 1 each time you add a user. As long as the salt is different for every user, rainbow tables would have to be recalculated for every user, making a brute-force attack extremely annoying. +1
-
Bart van Heukelom over 13 years@aqfire:
stored = hash(salt + pass)
. The dictionary may contains some input that results in a specific hash, but it would never be a salted password.invhash(stored).startsWith(salt) == false
if the salt is strong enough. -
niteria over 13 yearseveryone considering storing passwords in a database should read this blog post
-
Nathan Garabedian over 13 years@bart: my point is that if you have the PHP code and the database, a salted hash doesn't make much difference for a dictionary attack. You load the salt from the database or compute it from the PHP code, run each dictionary term through hash(salt+pass), and compare the result to stored.
-
Bart van Heukelom over 13 years@aqfire: I don't think dictionaries work like that. Say that my pass is
bar
, the salt isfoofoo
and the resulting hash is1234
. However, hashing the stringphp
also returns1234
. Now, assuming thatfoofoo
is a long and unique salt, it's much more likely thatdict(1234)
returnsphp
than that it returnsfoofoobar
. And there's no password that you can append tofoofoo
to formphp
. -
Nathan Garabedian over 13 years@bart You're talking about what this wikipedia page (en.wikipedia.org/wiki/Dictionary_attack) calls a "precomputed dictionary attack" where the dictionary words are directly converted to hash codes, but that's not the only way you can do a dictionary attack. What makes them so dangerous is that you can write a script to try all dictionary words against a login/password prompt if you have a small dictionary and enough time, and salt doesn't help in that case.
-
Donut over 9 years@Alisso: Here you go: news.ycombinator.com/item?id=2004833 I've also updated the answer with the new link.
-
MeVimalkumar almost 6 years@Matthew Salt (random string) is same or different for all users. If different, then do we need to store that in db?
-
Matt almost 6 yearsA different salt for each user is best.
-
metamonkey over 4 yearsNot sure why this answer was accepted. bcrypt is hashing, not encryption. The OP asked for encryption.