I’m using PHP. I want a safe and fast password encryption system. Hashing a password a million times may be safer, but also slower. How to achieve a good balance between speed and safety? Also, I’d prefer the result to have a constant number of characters.

The hashing mechanism must be available in PHP

It must be safe

It can use salt (in this case, are all salts equally good? Is there any way to generate good salts?)

Also, should I store two fields in the database(one using MD5 and another one using SHA, for example)? Would it make it safer or unsafer?

In case I wasn’t clear enough, I want to know which hashing function(s) to use and how to pick a good salt in order to have a safe and fast password protection mechanism.

EDIT: The website shouldn’t contain anything too sensitive, but still I want it to be secure.

EDIT2: Thank you all for your replies, I’m using hash(“sha256″,$salt.”:”.$password.”:”.$id) (EDIT 3: I haven’t used sha256 in some time now, see the accepted answer for a better mechanism)

@NSAwesomeGuy That depends on what you're using it for. It's trivial to rainbow-match or just brute force unsalted MD5 passwords, sure, but with decent salting it's still exceedingly impractical to build a rainbow table for fast cracking of sets of passwords, and brute force is a no-hoper.

TL;DR

Don’ts

Don’t limit what characters users can enter for passwords. Only idiots do this.

Don’t limit the length of a password. If your users want a sentence with supercalifragilisticexpialidocious in it, don’t prevent them from using it.

Never store your user’s password in plain-text.

Never email a password to your user except when they have lost theirs, and you sent a temporary one.

Never, ever log passwords in any manner.

Do’s

Use scrypt when you can; bcrypt if you cannot.

Use PBKDF2 if you cannot use either bcrypt or scrypt.

Reset everyone’s passwords when the database is compromised.

Why hash passwords anyway?

The objective behind hashing passwords is simple: preventing malicious access to user accounts by compromising the database. So the goal of password hashing is to deter a hacker or cracker by costing them too much time or money to calculate the plain-text passwords. And time/cost are the best deterrents in your arsenal.

Another reason that you want a good, robust hash on a user accounts is to give you enough time to change all the passwords in the system. If your database is compromised you will need enough time to at least lock the system down, if not change every password in the database.

Best practices

Bcrypt and scrypt are the current best practices. Scrypt will be better than bcrypt in time, but it hasn’t seen adoption as a standard by Linux/Unix or by webservers. If you are working with Ruby there is an scrypt gem that will help you out.

I highly suggest reading the documentation for the crypt function if you want to roll your own use of bcrypt, or finding yourself a goodwrapper or use something like PHPASS for a more legacy implementation. I recommend a minimum of 12 rounds of bcrypt, if not 15 to 18.

I changed my mind about using bcrypt when I learned that bcrypt only uses blowfish’s key schedule, with a variable cost mechanism. The latter lets you increase the cost to brute-force a password by increasing blowfish’s already expensive key schedule.

Average practices

I almost can’t imagine this situation anymore. PHPASS supports PHP 3.0.18 through 5.3, so it is usable on almost every installation imaginable—and should be if you don’t know for certain that your environment supports bcrypt.

But suppose that you cannot use bcrypt or PHPASS at all. What then?

Try an implementation of PDKBF2 with the minimum number of rounds that your environment/application/user-perception can tolerate. The lowest number I’d recommend is 1000 rounds.

As I Said Last Time…

The computational power required to actually crack a hashed password doesn’t exist. The only way for computers to “crack” a password is to recreate it and simulate the hashing algorithm used to secure it. The speed of the hash is linearly related to its ability to be brute-forced. Worse still, most hash algorithms can be easily parallelized to be reproduced even faster. This is why costly schemes like bcrypt and scrypt are so important.

You cannot possibly foresee all threats or avenues of attack, and so you must make your best effort to protect your users up front. If you do not, then you might even miss the fact that you were attacked until it’s too late… and you’re liable. To avoid that situation, act paranoid to begin with. Attack your own software (internally) and attempt to steal log in information, or access other user’s accounts. If you don’t you cannot blame anyone but yourself.

Lastly: I am not a cryptographer. Whatever I’ve said is my opinion, but I happen to think it’s based on good ol’ common sense … and lots of reading. Remember, be as paranoid as possible, make things as hard to intrude as possible, and then, if you are still worried, contact a white-hat hacker or cryptographer to see what they say about your code/system.

I think even emailing a new password in case of a lost one is a bad idea. Why not email a link to a secure site that allows the user to change his password? That's much more comfortable (no need to change the password after logging in with the random one) and more secure since no password went over the wire unencrypted - remember, users are lazy and chances are good that they do not change the random password.

Sending a temporary password through email that requires the user to change it the first time they use it and sending a "secure" link over email that allows them to set their password are equally risky. In either case anyone who intercepts the email can access the account as long as they use the link or password before the intended recipient does.

Recently I had a discussion whether password hashes salted with random
bits are more secure than the one salted with guessable or known
salts. Let’s see: If the system storing password is compromised as
well as the system which stores the random salt, the attacker will
have access to hash as well as salt, so whether the salt is random or
not, doesn’t matter. The attacker will can generate pre-computed
rainbow tables to crack the hash. Here comes the interesting part- it
is not so trivial to generate pre-computed tables. Let us take example
of WPA security model. Your WPA password is actually never sent to
Wireless Access Point. Instead, it is hashed with your SSID (the
network name- like Linksys, Dlink etc). A very good explanation of how
this works is here. In order to retrieve password from hash, you will
need to know the password as well as salt (network name). Church of
Wifi has already pre-computed hash tables which has top 1000 SSIDs and
about 1 million passwords. The size is of all tables is about 40 GB.
As you can read on their site, someone used 15 FGPA arrays for 3 days
to generate these tables. Assuming victim is using the SSID as
“a387csf3″ and password as “123456″, will it be cracked by those
tables? No! .. it cannot. Even if the password is weak, the tables
don’t have hashes for SSID a387csf3. This is the beauty of having
random salt. It will deter crackers who thrive upon pre-computed
tables. Can it stop a determined hacker? Probably not. But using
random salts does provide additional layer of defense. While we are on
this topic, let us discuss additional advantage of storing random
salts on a separate system. Scenario #1 : Password hashes are stored
on system X and salt values used for hashing are stored on system Y.
These salt values are guessable or known (e.g. username) Scenario#2 :
Password hashes are stored on system X and salt values used for
hashing are stored on system Y. These salt values are random. In case
system X has been compromised, as you can guess, there is a huge
advantage of using random salt on a separate system (Scenario #2) .
The attacker will need to guess addition values to be able to crack
hashes. If a 32 bit salt is used, 2^32= 4,294,967,296 (about 4.2
billion) iterations will can be required for each password guessed.

You missed what I originally wrote. I said to use a random nonce, stored with the record, PLUS the email address. The addition of the email address makes for extra entropy for the hacker to work on. I've since rewritten my answer in favor of bcrypt.

I’m using Phpass which is a simple one-file PHP class that could be implemented very easily in nearly every PHP project. See also The H.

By default it used strongest available encryption that is implemented in Phpass, which is bcrypt and falls back to other encryptions down to MD5 to provide backward compatibility to frameworks like Wordpress.

The returned hash could be stored in database as it is. Sample use for generating hash is: