
Salted Password Hashing – Doing It Right - pmoriarty
https://crackstation.net/hashing-security.htm#
======
eterm
Obligatory link to "How to safely store a password".

[http://codahale.com/how-to-safely-store-a-password/](http://codahale.com/how-
to-safely-store-a-password/)

~~~
tptacek
If you're simply trying to solve the problem of storing passwords, Coda's
article is better than this one.

~~~
sarciszewski
Yeah. I think defuse wrote that page 3-4 years ago and hasn't really
maintained it since then.

For PHP Devs, these are the two best options available:

* [https://github.com/ircmaxell/password_compat](https://github.com/ircmaxell/password_compat)

* [https://github.com/DomBlack/php-scrypt](https://github.com/DomBlack/php-scrypt)

------
kefka
Quoted:

"In step 4, never tell the user if it was the username or password they got
wrong. Always display a generic message like "Invalid username or password."
This prevents attackers from enumerating valid usernames without knowing their
passwords. "

Completely, utterly wrong. It 'looks' secure. We're not telling the user if
it's a login or password.

But if you try to create an account with the username, it tells you "CANNOT
CREATE, USER ALREADY EXISTS".

So, it's an anti-pattern that serves to confuse the user but fails to stop a
scripted attack.

~~~
amckenna
They are correct, it is important to show a generic message on the login. The
login is the first place an attacker will look for user enumeration. Just
because you may be able to find user enumeration somewhere else doesn't mean
you should leave it open everywhere.

For user enumeration on registration you have two choices:

1\. Provide a unique registration link to an email for signup. The prospective
user goes to the registration page, submits and email, checks their email,
then follows the link to a registration page. On this page it's easy to see if
someone is enumerating usernames, and every request contains the token from
their unique signup link so the token can be invalidated quickly.

2\. Provide a slow rate limited lookup to verify an existing username, every
2-3 seconds, which shouldn't negatively affect users as they signup because
they have to fill out a registration form in the mean time. This will however
greatly slow down a bot.

~~~
kefka
That still goes back to user interface implementation.

You can have a website that tells you "Bad password", and the user knows what
it is. They fix it, on they go.

If there's a bad actor, it says "bad password". Same warning. Now, you slow
down the response so that after 5-10 times, the website gets dog-slow. It's
still useful __if __you 're a legitimate user.

And if someone is script-attacking you, they're going to get an enumeration of
your usernames by using a different script. Or they'll dump the
username:passwd database if they get in. In that case, obfuscating usernames
is moot anyways.

It's security through obscurity, and it's not that obscure. And it's bad UI.

~~~
amckenna
It's defense in depth. Why tell a probable attacker that they have the correct
username if you can hide it from them. The less you give them the better. Most
users will know if they got their username incorrect, it's right there in
front of them on the screen.

The slowed down responses (exponential back-off) is a the preferred method of
rate limiting over account lockout.

 _" Or they'll dump the username:passwd database if they get in. In that case,
obfuscating usernames is moot anyways."_

Well if they can dump your DB it's game over for your whole app and possibly
infrastructure anyways.

------
sarciszewski
This page is old. Nowadays, PHP developers are encouraged to use
password_hash() and password_verify().

But if you want PBKDF2, defuse's github has more up-to-date code that, IIRC,
he hasn't taken the time to copy/paste into the page linked.

[https://github.com/defuse/password-
hashing](https://github.com/defuse/password-hashing)

~~~
McGlockenshire
If you want PBKDF2 and are using PHP >= 5.5, there's the hash_pbkdf2 function
built in to ext/hash, which should be available by default in most
installations.

There's also another implementation in PHP-CryptLib by Anthony Ferrara, the
gentlemen that brought the current password API to PHP.
[https://github.com/ircmaxell/PHP-CryptLib](https://github.com/ircmaxell/PHP-
CryptLib)

~~~
sarciszewski
The PBKDF2 code that defuse wrote will use hash_pbkdf2() if it's available. :)

------
FabianBeiner
If you’re using PHP 5.5+, this should be all you need to securely save
passwords:

    
    
      $sHash = password_hash('ThePassword', PASSWORD_BCRYPT, ['cost' => 12]);
    

Checking is as easily as:

    
    
      if (password_verify('ThePassword', $sHash)) {…}
    

Or do you guys have other thoughts?

~~~
kxo
base64 encode the password, reverse the encoded result.

~~~
cpach
What security advantage would that yield?

~~~
sarciszewski
[http://cryptofails.com](http://cryptofails.com) \-- I believe they're
referring to 46ESAB the worst password hashing algorithm ever

~~~
cpach
That’s hilarious! :D

------
dperfect
If you're using Rails (or ActiveRecord/ActiveModel specifically), you should
probably skip the suggested Ruby example and just use has_secure_password.
It's built in, uses bcrypt, and greatly reduces the likelihood of making
mistakes in a custom implementation.

------
atoponce
Overall, the article is solid. It is only missing key stretching with fast
cryptographic hashing algorithms like SHA-1 or SHA-2. Instead, it jumps right
into PBKDF2 and bcrypt.

That's fine, but it's not the full story, and as such, does a disservice for
those wanting to know about securely storing passwords on disk. PBKDF2 and
bcrypt can be a performance killer for authentication applications, if the
host is a busy host. Instead, changing the number of rotations on a fast
hashing algorithm gives you finer granularity on brute force speed versus
application and host reliability.

~~~
tptacek
Many of the largest, busiest applications in the world authenticate passwords
with bcrypt. Misplaced concern about its performance implications are as
approximately as old as the algorithm itself.

Hardware slices through iterations of fast hash functions --- functions that
are _designed to be quickly executed on hardware_ \--- like a red-hot knife
through warm butter. Developers who think they're getting "fine-grained
control" over brute force speed by tinkering with SHA2 are deluding
themselves. PBKDF2 is not simply the repeated application of SHA2.

Don't use fast hashes for passwords. Don't design constructions that use fast
hashes for passwords. That work has been done for you already: use scrypt,
instead of an underspecified, half-functional, and insecure homebrew
approximation of scrypt.

~~~
atoponce
Software library support for bcrypt and scrypt is still light (although there
is admittedly more support for the former than the latter). Which would could
cause you to roll your own in-house solution. This is less optimal. There is
ubiquitous library support for key stretching with fast hashes.

~~~
tptacek
Name a single widespread environment for which no secure password hash is
available.

~~~
atoponce
MySQL for bcrypt and/or scrypt.

~~~
tptacek
Plenty of applications built on MySQL use bcrypt. For example: see virtually
every Rails app.

~~~
atoponce
CREATE USER 'tptacek'@'db.example.com' IDENTIFIED BY 'su3rl337P@$sw0rd';

Let me know when MySQL supports bcrypt/scrypt.

~~~
tedunangst
I don't see how this is even relevant. What do MySQL's limitations have to do
with _your_ decision to store a password hash? Are you writing your entire
application _in_ MySQL?

~~~
ghshephard
I'm curious - is that a common practice with backend applications - writing
only with MySql application code and not using anything else like
Java/PHP/Python/C# etc?

I know Oracle has pretty sophisticated stored procedures, but even so, most of
the application logic of systems I've seen had program code written in an
"external" language.

~~~
tptacek
In 10 years of consulting with a large team for companies big and small I saw
a total of none applications built this way.

------
billturner
Page was timing out for me. Here's the google cache:
[http://webcache.googleusercontent.com/search?q=cache:S76Yg0P...](http://webcache.googleusercontent.com/search?q=cache:S76Yg0P_ArEJ:https://crackstation.net/hashing-
security.htm+&cd=1&hl=en&ct=clnk&gl=us)

------
drinchev
I like this article. If every developer read it. Significant part of any
database would be useless for attackers.

Cudos to the author.

------
solomone
Here's a node way of doing it: [https://github.com/solomon23/solomon-angular-
node-seed/blob/...](https://github.com/solomon23/solomon-angular-node-
seed/blob/master/server/utils/hash.coffee)

~~~
sehrope
From the link:

    
    
        ITERATIONS = 600
        ...
        crypto.pbkdf2 pwd, salt, ITERATIONS, LEN, (err, hash) ->
    
    

That's way too small for the number of iterations. Something like 100K would
be a better choice.

Alternatively here's a version that uses bcrypt:

    
    
        bcrypt = require 'bcrypt'
        rounds = Number(process.env.BCRYPT_ROUNDS || 12)
    
        module.exports =
          hash: (password, cb) ->
            bcrypt.hash password, rounds, cb
    
          compare: (password, hashedPassword, cb) ->
            bcrypt.compare password, hashedPassword, cb

~~~
solomone
From my testing 600 took about 15ms. 100k would take about 2.5 seconds.

~~~
sehrope
For me 600 iterations takes about 3ms (I guess my laptop is a bit faster). A
decent range to shoot for is .5-1 sec.

Test program:

    
    
        crypto = require 'crypto'
    
        password = 'testing'
        len = 128
        salt = crypto.randomBytes(len)
    
        iters = Number(process.argv[2] || 600)
    
        console.log 'Testing iters=%s', iters
        for i in [1..10]
          start = Date.now()
          crypto.pbkdf2Sync password, salt, iters, len
          elapsed = Date.now() - start
          console.log '   Test #%s - %s ms', i, elapsed
    

Output:

    
    
          $ coffee pbkdf2-test.coffee 100000
          Testing iters=100000
             Test #1 - 497 ms
             Test #2 - 510 ms
             Test #3 - 496 ms
             Test #4 - 525 ms
             Test #5 - 510 ms
             Test #6 - 493 ms
             Test #7 - 521 ms
             Test #8 - 518 ms
             Test #9 - 510 ms
             Test #10 - 498 ms
    
          $ coffee pbkdf2-test.coffee 10000
          Testing iters=10000
             Test #1 - 54 ms
             Test #2 - 50 ms
             Test #3 - 50 ms
             Test #4 - 55 ms
             Test #5 - 51 ms
             Test #6 - 52 ms
             Test #7 - 50 ms
             Test #8 - 49 ms
             Test #9 - 51 ms
             Test #10 - 50 ms
    
          $ coffee pbkdf2-test.coffee 600
          Testing iters=600
             Test #1 - 3 ms
             Test #2 - 3 ms
             Test #3 - 3 ms
             Test #4 - 3 ms
             Test #5 - 3 ms
             Test #6 - 4 ms
             Test #7 - 3 ms
             Test #8 - 4 ms
             Test #9 - 3 ms
             Test #10 - 3 ms

~~~
solomone
Nice, I was actually testing on my server hardware which is obviously lower
end. This is hopefully useful for people though.

For me 1 second seems pretty aggressive, that's CPU time/latency per login.

------
kerkeslager
The fact that we're still talking about password hashing shows how much we're
losing the war on security. Whether or not a service stores passwords
correctly cannot be verified by the user. There is no up-front cost to storing
passwords incorrectly, so sites have inadequate incentive to store their
passwords safely. As such users must use different passwords for every site.
This simply is too technical an issue for most users: no amount of education
will fix this, and password managers are too much work for most users. The end
result is that even if you hash your passwords correctly, your user's
passwords will be compromised on other systems and this will lead to accounts
on your system being compromised.

The solution to this is zero-knowledge password proofs[1]. Essentially, you
should never be passing your password to another party. Instead, you should be
providing the authenticating party with a prover (analogous to a public key in
asymmetric-key encryption, but based off your password) when you sign up. Then
when you want to authenticate, you provide the server with a proof based off
your password which exposes no information about your password but can be used
to verify that you have your password using the prover (analogous to a
signature in asymmetric-key encryption, but again based off your password).
There are a number of ways to generate these provers and proofs using existing
asymmetric-key algorithms such as RSA, ElGamal, or ECDSA.

Once this becomes pervasive, browsers and other network applications can
provide obnoxious security warnings similar to those currently provided for
untrusted certificates. This will create an up-front cost for people still
using password hashing and shame them into moving to ZKPP, while providing a
user-friendly way for users to tell if their password is being used
insecurely. Only once people are no longer giving their passwords to anyone
with a login page can we be sure that poor security on other sites won't
compromise our own servers.

EDIT: Additionally, ZKPP mitigates some of the damage caused by man-in-the-
middle attacks. Currently, if an attacker can gain access to communications
between a user and server, they can access the password in plaintext when it
is passing over the wire, even if it is adequately hashed on the server. This
has both permanence (the password continues to be useful after the
vulnerability is discovered--until the user changes their password) and wide
application (the password can be used on multiple sites).

With ZKPP the attacker only has access to the proof of the password. This
might allow the attacker to authenticate with the server during the attack.
However, a proper ZKPP implementation uses unique nonces from the server to
create the proofs and rejects non-unique proofs, so the proof cannot be used
after the vulnerability has been fixed (the server will check that it is
unique) and it cannot be used on other sites (the nonce is provided by the
server).

[1] [http://en.wikipedia.org/wiki/Zero-
knowledge_password_proof](http://en.wikipedia.org/wiki/Zero-
knowledge_password_proof)

~~~
tptacek
Ask me which I'd trust more: a website that stores passwords using whatever
hash a PHP developer came up with, or a website that implemented some kind of
PAKE, like SRP, to authenticate passwords.

What's especially ironic about your comment is that password-authenticated key
exchanges still require servers to store authenticators which can be attacked
offline. PAKEs actually make the problem we're talking about _harder_ , not
easier, because the authenticator format has to be compatible with the
protocol.

~~~
copsarebastards
> Ask me which I'd trust more: a website that stores passwords using whatever
> hash a PHP developer came up with, or a website that implemented some kind
> of PAKE, like SRP, to authenticate passwords.

If you're asking that question you don't understand the purpose of SRP or
ZKPP. If you're giving a password you use elsewhere to a website, you're
trusting them with your private data, and you shouldn't. As developers it's
our jobs to prevent users from making this mistake if we can.

When using SRP or another ZKPP scheme, trust doesn't enter the equation. You
aren't giving the server your password, you're giving them a piece of data
which is verifiably difficult to reverse to obtain your password, so you don't
need to trust them to store it properly.

> What's especially ironic about your comment is that password-authenticated
> key exchanges still require servers to store authenticators which can be
> attacked offline.

This isn't ironic: this is exactly what I would expect from kerkelsager's
comment. Again you're just showing your ignorance. Yes, you can attack an
authenticator. The point of ZKPP is that it allows the client to verify that
such an attack is computationally difficult. If you've given the server your
password in plain text (or reversibly encrypted form), you can no longer
verify that obtaining the password from the authenticator is computationally
difficult. For all you know, the server stores your password in plain text.
This isn't a theoretical problem: there are numerous examples of this
happening.

> PAKEs actually make the problem we're talking about harder, not easier,
> because the authenticator format has to be compatible with the protocol.

If you're objecting to implementing things correctly because it's hard,
software might not be the right career for you.

~~~
tptacek
It's always possible that I just don't know how SRP works, I suppose. Or: it's
possible that I'm right, and that using a PAKE to handle simple logins to web
and mobile applications is a really bad idea that doesn't make any of the
problems we're talking about on this thread easier.

~~~
copsarebastards
SRP solves a problem that password hashing doesn't solve. _Obviously_ SRP is
harder to implement than password hashing. If you're still talking about which
is "easier" you're still missing the point of SRP (and ZKPP).

~~~
tptacek
You're missing me. SRP doesn't solve the password storage problem --- its
authenticators are crackable --- and introduces new ones. SRP does not address
the problem we're talking about on this thread. We don't have password storage
problems simply because applications don't use "ZKPPs" and PAKEs.

~~~
copsarebastards
There is no good solution to the "password storage problem", as you call it.
The fundamental issue is that "storing passwords securely" is the wrong
problem to solve.

Think from the perspective of a non-technical user. You're lazy, so you want
to use a short password that's easy to remember, and you want to use it on
every website.

Now think from the perspective of a software engineer. Your non-technical
users will use short passwords that are easy to remember and therefore easy to
break. And they will use that password on every website. So no matter how you
store your user's passwords, some idiot server administrator somewhere will
store those same passwords in plain text, and they'll get hacked, and the
hackers will have access to your user's passwords.

So the problem isn't "how do I store passwords securely", it's "how do I
prevent my users from giving their passwords to an idiot server administrator
who won't store them securely".

And as a technical user, I also have this problem, which is "how do I know if
my server administrator is an idiot and is storing my passwords in plain
text?" The solution to this is to use different high-entropy passwords on
different sites, but this is tedious.

If ZKPP (zero-knowledge password proof) is widely adopted and supported in
browsers, it solves both the server administrator's problem and the technical
user's problem:

1\. It stops idiot server administrators from storing passwords in plain text.
The "zero knowledge" part of ZKPP means that users never give server
administrators their password or any knowledge about their password, so
administrators can't store the password stupidly.

2\. As a technical user, I can now use the same password on multiple sites,
provided a) my password is significantly entropic and b) all those sites use a
ZKPP scheme. Since I never give the password to any site, I don't have to
worry that one of them will store my password stupidly. My browser does the
job of providing different authenticators to different sites, so breaking one
authenticator doesn't break all of them.

3\. A non-technical user with a low-entropy password on multiple still runs
the risk that their password will be broken one one site, breaking it for all
sites. However, the non-technical user is still better off because a) their
password is at least properly hashed on all sites, rather than being stored in
plain text, which will make an attack more costly, and b) caching password
hashes amortizes the performance hit of password hashing, so we can afford to
use more secure hashes, which again makes an attack more costly.

NOTE: I don't know the details of SRP well enough to know whether all these
benefits apply. I do know that SRP claims to provide a ZKPP, so I'll
tentatively lump it in with ZKPPs, but I haven't actually verified the
algorithm personally so I can't speak to its security.

~~~
tptacek
_2\. As a technical user, I can now use the same password on multiple sites,
provided a) my password is significantly entropic and b) all those sites use a
ZKPP scheme. Since I never give the password to any site, I don 't have to
worry that one of them will store my password stupidly. My browser does the
job of providing different authenticators to different sites, so breaking one
authenticator doesn't break all of them._

Go through the exercise of cracking an SRP authenticator to see the problem
with this logic.

Weirdly, despite saying upthread that I must not know how SRP works to say
what I'd said, you now say that you're not familiar with SRP. If it helps,
substitute your favorite password protocol instead of SRP. I believe the
problem will be the same.

If not: happy to learn something new.

~~~
copsarebastards
> Go through the exercise of cracking an SRP authenticator to see the problem
> with this logic.

There are mathematical proofs of SRP's security. Yes, there are some issues
with some _implementations_ of SRP, but those are problems with
implementations, not with SRP. Frankly, this is just one of those arguments at
this point where it's clear you don't actually understand the fundamentals of
security and you're too committed to your argument and too proud to admit you
don't know. Since you can't actually make an argument, you're just presenting
me with difficult/impossible tasks and claiming that if I did them it would
prove your point, knowing full well that I won't do them. If you actually were
knowledgeable on this topic, you could explain it: I have explained everything
I said so far.

> Weirdly, despite saying upthread that I must not know how SRP works to say
> what I'd said, you now say that you're not familiar with SRP.

I don't know all the exact implementation details of SRP, but I _do_
understand the general architecture. The problem is that the devil in security
is often in the details. I know enough to know the limitations of my
knowledge. The important thing for the sake of what I'm saying is that I know
what problem SRP is trying to solve.

> If not: happy to learn something new.

The only thing I'm going to try to teach you at this point is this: if you're
going to even bother forming an opinion on something technical, you had better
be _damn_ sure you're right. Because if you're wrong, it's human nature to
react negatively when someone tells you you're wrong, so you'll try to argue
about something you're wrong about, and the result is: you'll never learn, and
you'll probably impress some people who know even less than you, but you'll
embarrass yourself in front of anyone who actually knows what they're talking
about, and you'll limit your progress as an expert in your field. You'll never
get smarter until you stop trying to prove how smart you already are.

------
Sami_Lehtinen
Whole hashing and salting etc stuff is pointless. What does matter? Is the
fact that passwords aren't reused anyway. Do I really care if you know that my
password for service X was: c'EyqXnrq-bCyfF_dK67$j I don't really couldn't
care less, if you get it hashed or not, really. If they owned the system, and
they were after my data, they got it already. Password(s) is just minute and
meaningless detail. I've always wondered this pointless discussion about
passwords. It just doesn't make any sense.

~~~
tedunangst
> Is the fact that passwords aren't reused anyway.

Completely wrong. People reuse passwords all the time.

