What are the best practices to encrypt passwords stored in MySql using PhP?

16,083

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.

Share:
16,083
Admin
Author by

Admin

Updated on July 26, 2022

Comments

  • Admin
    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.

    1. The kiddie has a complete copy of the database.
    2. 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
    eaj over 13 years
    Yep, 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
    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
    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
    Admin over 13 years
    Assuming 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
    Admin over 13 years
    Won't prevent dictionary-attacks.
  • Admin
    Admin over 13 years
    They 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
    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
    Admin over 13 years
    And if your really don't care about time, or your server is a supercomputer: simply set the work factor to 10000.
  • Nathan Garabedian
    Nathan Garabedian over 13 years
    You 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
    Nathan Garabedian over 13 years
    This 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
    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
    niteria over 13 years
    everyone considering storing passwords in a database should read this blog post
  • Nathan Garabedian
    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
    Bart van Heukelom over 13 years
    @aqfire: I don't think dictionaries work like that. Say that my pass is bar, the salt is foofoo and the resulting hash is 1234. However, hashing the string php also returns 1234. Now, assuming that foofoo is a long and unique salt, it's much more likely that dict(1234) returns php than that it returns foofoobar. And there's no password that you can append to foofoo to form php.
  • Nathan Garabedian
    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
    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
    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
    Matt almost 6 years
    A different salt for each user is best.
  • metamonkey
    metamonkey over 4 years
    Not sure why this answer was accepted. bcrypt is hashing, not encryption. The OP asked for encryption.