3

I already asked this on StackOverflow but I was suggested this would be a better suited place and I found no way to "move" the question so I have to copy it.

Believe, I know, there are hundreds questions about this issue. I read looots of them to find the SAFEST way to store a password but every answer given gives me that itchy sensation that we still can do much better. And I'm planning to use this in PHP but I think it applies to any coding language.

I'm no security expert but isn't only one method not secure enough?

ENCRYPT

If I understood correctly Encrypted password (with PHP) can be Decrypted back to plaintext if you know the string which was used. Most hosting accounts I've seen hacked first get access to the files and then looking for the DB password stored in config.php or so.

Even if I use a big and complicated string to encrypt the password, if the intruder goes to create_account.php (or wherever the user is created) s/he will find the string there and would be able to decrypt the whole password column in a few minutes (right?)

HASHING

The good thing about hashing is that the password can't be returned to plain text, which is great but... Many hashing methods have exploits and I've been reading some articles that getting possible values for a hashing (a hash string can mean more than one plain text string). I read an article that said that some people were able to recover password from hashed using Amazon EC2 in seconds for weak passwords and hours for strong ones.

I even saw hashing index directories where hackers and random j*rks store huge amounts of hash>string values so if you enter the hash you got, you can get possible password in just one second. This is still some kind of dictionary attack but a pretty effective one.

For common hashed like MD5 I even saw an article in which a hacker easily found that Google somehow indexes MD5 hashes because of how much it is used (like in filenames, gravatar.com and alike APIs, etc) so he had an indexed HUUUGE dictionary of hashed that only take one second to associate to values.

MD5 and SHA1 were once believed to be secure but then exploits appeared. What if the trusted method used now (I read blowfish is the best one, is that correct?) gets some exploit in the future?

And lastly, I know this is ridiculous but I'm uncomfortable with the possibility of 2 passwords having the same hash result. I know, the possibilities of somebody guessing the other value that has the same hash is veeeeeery low, but HEY, we're in 2012!, weren't we supposed to have flying cars and computers that could render Avatar movie in an hour already? :P There should be something better already...

SALT

Even if I use a salt to make it harder for password retrieval, if the hacker sees the fixed salt string (or where the random salt was stored) it wouldn't make much more complicated to brute force with a cloud computational attack, would it?

ISN'T THERE SOMETHING BETTER?

As I said, I'm far from being a security expert but isn't there any option out there? I've read lots of questions about this here in Stack Overflow and Google result (which most are stack overflow answers :D ) and they pretty much always say the same thing.

If hashing and encryption are the only options, is a way to make it better? Is there a way to hide the encryption password in the case the file that creates the users gets accessed?

In all those answers I never saw somebody suggesting to combine methods like:

sha1(crypt[blowfish.salt],salt) Is this a bad idea for some reason? (I can only think of being too processor consuming in a site with thousands of users registering and loggin-in, but I can handle that in the sites I'm planning to use it)

I'm not asking "please code this for me", I want to know how to make it "as most impossible possible" (hardest :P) to get the passwords in the case the DB and Source Code got in the wrong hands

Maybe I misunderstood the way these methods work and maybe there's something like a PHP function to make a string undecryptable even with the password, or maybe I missed something. I still find it strange that nobody suggested a combination of methods.

Thanks in advance and thanks again if you're still reading my worried mind's thoughts

AviD
  • 72,138
  • 22
  • 136
  • 218
Juan
  • 141
  • 1
  • 4
  • 2
    For future reference, you can use the "flag" button on a question to ask a moderator to migrate it. – Iszi Feb 22 '12 at 15:47
  • 2
    There has been plenty of discussion regarding this lately and the common consensus seems to be bcrypt is a quite solid choice. Just use a big enough work factor with it. Leaving this as a comment since I'm just pointing out this and perhaps someone else has something actually useful to say :) –  Feb 22 '12 at 07:34
  • Thanks Jani. Any thoughts if hasing the bcrypt result would be better? – Juan Feb 22 '12 at 07:36
  • 2
    Almost certainly not... don't has something that has already been hashed or encrypted because it reduces the entropy to that of the lowest hashing method, and exposes you to much more risk of a clash. –  Feb 22 '12 at 07:41
  • Yes, there is something better. :-) Use a variable salt, that is different for each user. Then your hash won't be weak for rainbow attacks. –  Feb 22 '12 at 07:41
  • But if I store the variable string salt in the DB (which we're supposing the hacker already has) or use a constant like the user's email address (which the hacker would see in the source code) the salt doesn't add much security... or does it? – Juan Feb 22 '12 at 07:55
  • Well, if the hacker has *both* the code and the DB, then, yes, the salt won't add *that* much security. Still, it is better than nothing, as the hacker needs to set up separate rainbow tables for each user to crack the hash. It will cost him more effort and CPU time, but he will be able to crack all weak passwords eventually. May I suggest separating the code and the DB on two physical servers? –  Feb 22 '12 at 08:33
  • 2
    @Aron, that is incorrect. bcrypt already includes a per-hash salt, and besides salts have nothing to do with secrecy. Contrary to what Juan wrote, protecting the secrecy of the salt is unimportant - in principle this can be public information, as long as its *random and unique* per user. The *entire purpose* of salts is to prevent rainbow table attacks. And note that this is not a trivial purpose. – AviD Feb 23 '12 at 11:21
  • 1
    I want to reiterate what AviD said above. Salts are not required to be kept secret. Salts protect your password database against rainbow attacks, where an attacker has a large table of precomputed password hashes. – Stephen Touset Sep 26 '12 at 17:07

11 Answers11

7

Do not encrypt passwords - (one-way) hash them.

You should not need or want to decrypt passwords. If you do, you aren't really that concerned about security.

Security is always a trade-off with performance.

Standard hash algorithms (md5, sha1) are secure unless the salt becomes known to the attacker.

Using a slow hashing algorithm will make passwords secure even if an attacker has access to the code and the stored hash.

The crypt function is probably what you want in php. Using a high number of encryption rounds will make calculating the hash of a given input slow; that will make it infeasible to brute-force but also make it slow for you/your application to verify that the password entered by the user is the same as the hash you have stored - that's the trade-off.

Doubling up techniques is not going to do anything to increase security - the encryption rounds parameter in the salt means it is feeding the output of hashing the input string back into the hashing algorithm that many times - You're proposing a bespoke +1, just add one to the number of rounds you want to execute. In addition, for the example given sha1(crypt[blowfish.salt],salt) this would prevent you from being able to compare the hash to the user input correctly - from The first example:

You should pass the entire results of crypt() as the salt for comparing a password

Without the output of crypt, you cannot compare user input to the already-generated hash.

AD7six
  • 238
  • 1
  • 3
  • 6
  • Encrypting passwords is the most secure (and therefore least useful) way of storing them - if you immediately throw away the key. – Fax Aug 27 '19 at 08:43
7

You could delegate the authentication to a different server inside your network, which your primary server talks to through a webservice or whatever protocol you want. This way a hacker can access your primary server and files all they want and still not get anything.

The authentication server can be locked down much tighter, minimizing the surface area for attack -- not to mention that the attack would have to happen via your primary server.

Supr
  • 184
  • 5
  • One may also use various forms of "hardware security module". An advantage of an HSM is that it may be possible to provide a means of storing plain-text passwords in a way that could not be breached without physical access to the HSM, but in a way that would allow people with physical access to convert them for use on another HSM that would store and exchange them differently. – supercat May 29 '18 at 22:11
7

Safest password storage scheme? A strong slow-hash (like bcrypt) with several byte random salt that prevents attacking the passwords in parallel with a rainbow table. Encryption of passwords should only be used when absolutely necessary: e.g., you are recording a password to an external system that you do not control that is too dumb to use a sane protocol of granting third party access (like openauth), so you need to record the users password on that system.

And lastly, I know this is ridiculous but I'm uncomfortable with the possibility of 2 passwords having the same hash result. I know, the possibilities of somebody guessing the other value that has the same hash is veeeeeery low, but HEY, we're in 2012!, weren't we supposed to have flying cars and computers that could render Avatar movie in an hour already? :P There should be something better already...

Yes, your uncomfortableness is ridiculous. If you use say SHA256 (256 bits) or bcrypt (184 bits), the chance of getting two random passwords going to the same hash is extraordinarily improbable (about 1066 times less probable than winning the powerball jackpot). For SHA256 all passwords should be unique until you have about 2256/2 ~ 1038 passwords stored in the table and then about half of the time they'll be one set of duplicate passwords. To get it so any given password likely has a second string that also maps to the same hash, you'd need roughly 2256 ~ 1077. (All these are under the assumption that the hash functions have good uniformity).

Let's put these large numbers in perspective. The mass of the Earth is about 6x10^24 kg. So if you use on average 50 bytes to store each password in a rainbow table, before there's likely one duplicate with SHA256 you'd need about 10^30 gigabytes of data. If you put this in terms of all 3 TB hard drives that each weigh about half a kg, your hard drives would weigh about 1000 times more than the Earth. Or as comparison for having it likely that most hashes have multiple strings, compare the 1077 number of SHA256 hashes necessary to being a billion times less than the number of atoms in the Milky Way Galaxy (roughly 1068).

dr jimbob
  • 38,768
  • 8
  • 92
  • 161
6

Right - first up, there an awful lot of questions here, so while I'll have a quick go, you should probably first read the questions linked at the side, and those with the and tags. Many of your questions are answered there. In fact, they all are except for your specific question on where you should store the encryption key.

A quick summary:

  1. PHP - generally regarded as quite insecure. It can be secured to various levels, but the structure and functional bloat that often comes with it means that if security is your key aim, the risks should be very carefully appreciated.
  2. Don't store your encryption keys in with public data. In a fully hosted environment this may be tricky to manage, but it is a major risk.
  3. Yes, rainbow tables will let you retrieve passwords from hashes in seconds if you don't use salting - hashes should use salts and a slow algorithm. This can be done very securely (ie brute forcing will take an unfeasibly long time - longer than the life of the universe, for example)
Rory Alsop
  • 61,367
  • 12
  • 115
  • 320
3

Yes there are "hundreds" of questions about this issue. This compelled me to write an article on it that I hope will answer it once and for all:

http://upokecenter.dreamhosters.com/articles/2012/02/secure-password-storage-for-web-sites/

Essentially, secure password storage involves:

  • The right algorithm - use bcrypt or PBKDF2, both slow algorithms that hash passwords with their own "salt". Encryption is not ideal because the exact password can be recoverable if the key is known, which is not necessarily the case for hashing. MD5, SHA-1, etc. by themselves are not ideal either, because they're not "slow" enough.

  • The right password policy - such as forcing users to use long passwords, and encouraging strong passwords.

Please add comments to help me improve that article.

Peter O.
  • 131
  • 3
  • "_bar further logins after too many failed tries_" which allows a trivial DOS against anyone knowing only the username! You have to weigh the DOS risk against the online password brute-force risk. – curiousguy Aug 27 '12 at 15:33
  • That's right! But that's only the case when a hacker is thereby preventing a user from logging in *forever*, with no other means of recovering the ability to log in. This is why, in general, some Web sites offer a way to call the user by mobile phone, show a CAPTCHA image, show a security question, or use other means to make logging in more difficult after a certain number of attempts. Some sites, such as Google and Battle.net, also offer the option of logging in with not just a password, but a unique random code. – Peter O. Aug 27 '12 at 16:19
  • 1) Actually the intention of the salt is not to make dictionary attacks more difficult, the salt is **not** secret and can be added in a dictionary attack. The salt should prevent using a single rainbowtable to crack all passwords. What you mean is called pepper. 2) Instead of barring users from login, it could be better to slow down the possible attempts. Otherwise it's easy to do some false login attempts, so the user cannot use the service anymore. – martinstoeckli Aug 28 '12 at 07:09
3

You should be hashing your passwords (with HMAC+bcrypt), not encrypting them. Here are some general rules I follow:

  1. Use an algorithm that provides sufficient time constraints (i.e., bcrypt). This is because, bcrypt allows you to customize the work required to compute each hash which helps reduce the number of brute force attempts an attacker can preform in a given time frame.

  2. Use per user salts. By using per user salts, you can be sure no users with the same password will have the same hash. It also reduces your risk associated with rainbow tables.

  3. Use a system wide salt, that is not stored in the database. This provides an extra layer of security in the event of a full DB compromise where both password hashes AND salts are compromised. By using another salt that is only stored in memory, an attacker would not be able to easily brute force each users password.

Gerry
  • 366
  • 1
  • 4
1

You should hash passwords that are stored in the database with a salt. I would not try to write my own password hashing library. A very popular one that is used in many PHP projects (including mine) is phpass from http://www.openwall.com/phpass/ - it can create salted hashes using bcrypt, DES, or MD5.

To help make your users create safer passwords you can require them to make passwords of a certain length or complexity.

pyasi
  • 111
  • 2
1

First of all, you should always follow the principle of least privilege. Although not directly applicable in this case, it should mean that unless you definitely need the original password (e. g. remote authentication), don’t use a scheme that allows to retrieve the original password.

So encryption does only come into consideration if you definitely need to get the original password. Otherwise, only store a hash of the password.

As for the hashing scheme, you should use one that was specifically designed for hashing/storing passwords (e. g. crypt). In addition to general purpose cryptographic hash functions like those of the MD or SHA families, many of them add an additional and adjustable time cost factor that makes brute-force attacks less efficient/harder.

Speaking of brute-force attacks, use a unique salt for each hashed password. Some password schemes like crypt do already apply automatic salting. Use it and don’t try to invent your own system.

Gumbo
  • 2,003
  • 1
  • 13
  • 17
0

I can give you quickly the function I use to hash passwords (the key is, of course, modified here) :

$siteLongKey = '5efd8123878793f88bf1362905ae209794a8602f92969a67862b925fd6e50b04dfd31ae74002cb449824fcaa90e2efd096e8653a3f52e2b048e24b289c705129';

    function hashPassword($password, $nonce = '') {
      global $siteLongKey;    
      return hash_hmac('sha512', $password . $nonce, $siteLongKey);
    }

This is pretty good, $siteLongKey is your global key and the $nonce is the salt you can add. It could be the email address or the date the account was created, or any static information related to the account. In this case, every user has a different salt. This is one-way hashing and (in theory) there's no password recovery but only password reset.

-2

The best algorithm currently available is bcrypt. However, a better algorithm was invented in November and a patent application is being slowly prepared for it. The algorithm may be available in no less than 18 months (WIPO mandatory non-disclosure period). Free usage will be available only with non-commercial Linux distributions.

nlovric
  • 321
  • 5
  • 16
-2

The best option to store passwords in db is one way encryption using Message-Digest-Algorithm that produce 128-bit hash value, and cannot be decrypted. So password retrieval is not possible. To authenticate any user we always create hash of the password supplied and compare both hash values.

If you are using custom algorithms to encrypt and decrypt the password use ionCube to use binary codes of your algorithms. so even if hacker gets into the code, it will be of no use. you can check it here http://www.ioncube.com/ this is another best way to go safe..

  • warren Message-Digest-Algorithm is MD5 –  Feb 22 '12 at 07:55
  • 1
    MD5 is *a* message digest algorithm, that's true - however, it is not the only one. And either way, MD5s are trivial to mimic/crack compared to most other recommended one-way hash algorithms – warren Feb 22 '12 at 07:59