itemprop="text">
It is currently said that MD5 is
partially unsafe. Taking this into consideration, I'd like to know which mechanism to
use for password protection.
This question,
href="https://stackoverflow.com/questions/348109/is-double-hashing-a-password-less-secure-than-just-hashing-it-once">Is
“double hashing” a password less secure than just hashing it once?
suggests that hashing multiple times may be a good idea, whereas href="https://stackoverflow.com/questions/55862/how-to-implement-password-protection-for-individual-files#55904">How
to implement password protection for individual files? suggests using
salt.
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.
Related questions that
don't quite cover my question:
href="https://stackoverflow.com/questions/157998/whats-the-difference-between-sha-and-md5-in-php">What's
the difference between SHA and MD5 in PHP
href="https://stackoverflow.com/questions/30946/simple-password-encryption">Simple
Password Encryption
href="https://stackoverflow.com/questions/198803/secure-methods-of-storing-keys-passwords-for-asp-net">Secure
methods of storing keys, passwords for asp.net
href="https://stackoverflow.com/questions/205153/how-would-you-implement-salted-passwords-in-tomcat-5-5">How
would you implement salted passwords in Tomcat 5.5
DISCLAIMER: This answer was written in
2008.
Since then, PHP has given us href="http://php.net/manual/en/function.password-hash.php"
rel="noreferrer">password_hash
and href="http://php.net/manual/en/function.password-verify.php"
rel="noreferrer">password_verify
and, since their
introduction, they are the recommended password hashing & checking
method.
The theory of the answer is
still a good read
though.
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.
- Don't strip or escape HTML and
special characters in the password.
- 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.
- Never hash passwords with href="http://arstechnica.com/security/2012/12/oh-great-new-attack-makes-some-password-cracking-faster-easier-than-ever/"
rel="noreferrer">SHA1 or MD5 or even SHA256! href="http://securityledger.com/new-25-gpu-monster-devours-passwords-in-seconds/"
rel="noreferrer">Modern crackers can exceed 60 and 180 billion
hashes/second (respectively).
- Don't mix href="http://blog.ircmaxell.com/2015/03/security-issue-combining-bcrypt-with.html"
rel="noreferrer">bcrypt and with the raw output of
hash(), either use hex output or base64_encode it. (This applies to any input
that may have a rogue \0
in it, which can seriously weaken
security.)
Dos
- Use
scrypt when you can; bcrypt if you cannot.
- Use PBKDF2 if
you cannot use either bcrypt or scrypt, with SHA2
hashes.
- Reset everyone's passwords when the database is
compromised.
- Implement a reasonable 8-10 character
minimum length, plus require at least 1 upper case letter, 1 lower case letter, a
number, and a symbol. This will improve the entropy of the password, in turn making it
harder to crack. (See the "What makes a good password?" section for some
debate.)
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.
Jeremiah Grossman, CTO of Whitehat
Security, href="https://www.whitehatsec.com/blog/cracking-aes-256-dmgs-and-epic-self-pwnage/"
rel="noreferrer">stated on White Hat Security blog after a recent password
recovery that required brute-force breaking of his password
protection:
Interestingly, in living out this nightmare, I learned A LOT I didn’t know
about password cracking, storage, and complexity. I’ve come to appreciate why
password storage is ever so much more important than password complexity. If you don’t
know how your password is stored, then all you really can depend upon is
complexity. This might be common knowledge to password and crypto pros, but
for the average InfoSec or Web Security expert, I highly doubt
it.
(Emphasis
mine.)
What makes a good
password anyway?
href="http://xkcd.com/936/" rel="noreferrer">Entropy. (Not that I fully
subscribe to Randall's viewpoint.)
In short,
entropy is how much variation is within the password. When a password is only lowercase
roman letters, that's only 26 characters. That isn't much variation. Alpha-numeric
passwords are better, with 36 characters. But allowing upper and lower case, with
symbols, is roughly 96 characters. That's a lot better than just letters. One problem
is, to make our passwords memorable we insert patterns—which reduces entropy.
Oops!
Password entropy is href="https://ritcyberselfdefense.wordpress.com/2011/09/24/how-to-calculate-password-entropy/"
rel="noreferrer">approximated easily. Using the full range of ascii
characters (roughly 96 typeable characters) yields an entropy of 6.6 per character,
which at 8 characters for a password is still too low (52.679 bits of entropy) for
future security. But the good news is: longer passwords, and passwords with unicode
characters, really increase the entropy of a password and make it harder to
crack.
There's a longer discussion of
password entropy on the href="https://crypto.stackexchange.com/questions/374/how-should-i-calculate-the-entropy-of-a-password">Crypto
StackExchange site. A good Google search will also turn up a lot of
results.
In the comments I talked with
@popnoodles, who pointed out that enforcing a password policy of X
length with X many letters, numbers, symbols, etc, can actually reduce entropy by making
the password scheme more predictable. I do agree. Randomess, as truly random as
possible, is always the safest but least memorable
solution.
So far as I've been able to tell,
making the world's best password is a Catch-22. Either its not memorable, too
predictable, too short, too many unicode characters (hard to type on a Windows/Mobile
device), too long, etc. No password is truly good enough for our purposes, so we must
protect them as though they were in Fort
Knox.
Best
practices
Bcrypt and href="http://www.tarsnap.com/scrypt.html" rel="noreferrer">scrypt are the
current best practices. rel="noreferrer">Scrypt will be better than bcrypt in time, but it hasn't
seen adoption as a standard by Linux/Unix or by webservers, and hasn't had in-depth
reviews of its algorithm posted yet. But still, the future of the algorithm does look
promising. If you are working with Ruby there is an href="http://rubygems.org/gems/scrypt" rel="noreferrer">scrypt gem that
will help you out, and Node.js now has its own href="https://npmjs.org/package/scrypt" rel="noreferrer">scrypt package.
You can use Scrypt in PHP either via the href="https://pecl.php.net/package/scrypt" rel="noreferrer">Scrypt
extension or the href="https://paragonie.com/book/pecl-libsodium/read/07-password-hashing.md"
rel="noreferrer">Libsodium extension (both are available in
PECL).
I highly suggest reading the
documentation for the crypt
function if you want to understand how to use bcrypt, or finding yourself a
href="https://stackoverflow.com/questions/4795385/how-do-you-use-bcrypt-for-hashing-passwords-in-php/6337021#6337021">good
wrapper or
use something like rel="noreferrer">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. rel="noreferrer">PHPASS supports PHP 3.0.18 through 5.3, so it is usable on
almost every installation imaginable—and should be used 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 href="http://www.itnewb.com/tutorial/Encrypting-Passwords-with-PHP-for-Storage-Using-the-RSA-PBKDF2-Standard"
rel="noreferrer">PDKBF2 with the href="https://security.stackexchange.com/questions/3959/recommended-of-iterations-when-using-pkbdf2-sha256">maximum
number of rounds that your environment/application/user-perception can
tolerate. The lowest number I'd recommend is 2500 rounds. Also, make sure to use href="http://php.net/hash_hmac" rel="noreferrer">hash_hmac() if it is
available to make the operation harder to
reproduce.
Future
Practices
Coming in PHP 5.5 is a href="http://php.net/manual/en/ref.password.php" rel="noreferrer">full password
protection library that abstracts away any pains of working with bcrypt. While
most of us are stuck with PHP 5.2 and 5.3 in most common environments, especially shared
hosts, @ircmaxell has built a rel="noreferrer">compatibility layer for the coming API that is backward
compatible to PHP 5.3.7.
Cryptography Recap
& Disclaimer
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 perform
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 user credentials, or modify other user's
accounts or access their data. If you don't test the security of your system, then 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.
No comments:
Post a Comment