11

My understanding of the standard best practice way to handle passwords is:

  1. Establish a secure encrypted connection between client and server.

  2. Client sends password in plaintext over this encrypted connection.

  3. Server gets plaintext password.

  4. Server adds a salt to it (and possibly a pepper) and hashes it.

  5. Server stores it (in case of registration) or compares it with the corresponding stored hash (in case of login).

The hashing, plus salting, etc., ensures that, even in a password database leak, in theory, everything is secure. Due to the salts we can't match the passwords with each other to figure out which are the same, etc.

However, another common issue with password database leaks (which is also solved here) is when a less secure service's database leaks, but many people used the same password for other services. This is still handled by salts, peppers, etc. (as much as possible).

However, I am concerned about step 3 -- briefly the password exists in plaintext in memory on the server. If even one sysadmin is malicious, or if the server is compromised, the plaintext passwords may leak. Of course, if the server is compromised, all bets are off on this service being secure. However, in case that users used the same password as they would for another service (as many people do), they are affected by this.

My question is why isn't the standard practice to have the user also hash the password with salt (+ pepper, potentially) and send that hash over? Then, even if the server is compromised, this leaks no information about the user's original password. Then, in order to protect this service if only the database leaks, we can still do the hashing with salting, etc. on the server side as well and store that.

I am considering this in the context of the client being a binary executable, not a web app. So the client code is shipped separately, not from the same server as the one processing login requests.

3

2 Answers 2

28

Hashing the password both client-side and on the server has few benefits, some major issues and strong competition from clearly superior alternatives like Password-authenticated Key Exchange (PAKE) or completely passwordless options like WebAuthn (using passkeys). This might explain why it's not very popular.

First, the only reason that a user wants to hide their plaintext password from the application they're authenticating at is password reuse. Otherwise, it makes no difference whether the user sends their password or some password-derived hash to the server. From the server's perspective, they're both credentials. And an attacker who manages to obtain the client-side hash can immediately use to to gain access to an account, regardless of whether or not they know the underlying password. Of course there is a difference if the plaintext password also works for other services, and password reuse is a real problem. However, client-side hashing doesn't fully solve this. If the password is weak, then it an attacker may still be able to brute-force the hash. The actual solution is for the user to generate strong and unique passwords with, e.g., a password manager.

Secondly, client-side hashing doesn't come for free. Modern password hashing algorithms like Argon2 are computationally expensive by design and require a lot of memory. On a server, you know the hardware and can find a good balance between the strength of the resulting hash, the use of available resources and the time a user has to wait. Servers also tend to enough CPU cores and RAM to use reasonably strong cost parameters. This is much more difficult when you run the hashing on a client device. You may be dealing with weak hardware, you might not even know the exact hardware, and so it will be tricky to find the right balance. If you set the cost parameters too high, this can overload the system, which is a real problem if the user needs the system resources for other tasks. If they're too low, the resulting hash is weak.

Last but not least, there are already much better alternatives to classical password hashing. A lot of online services now offer WebAuthn which completely replaces passwords with strong public-key authentication. Depending on the userbase, mutual TLS with client certificates might be another option. And if you want to keep using passwords but avoid the problems of classical password hashing, there are PAKE algorithms like OPAQUE or the older Secure Remote Password which let the user completely hide the password from the server. Even if none of those are an option, you can probably still strengthen the password-based authentication with a second factor (e.g., TOTP tokens).

6
  • 1
    What are your thoughts on SCRAM-SHA-*, both in terms of security vs the other options and vs sending a plain pasword? I recall part of its design was that the client would only do the KDF operation once and store it instead of the password, and the server could also verify it without having to do any KDF operations on the server-side at all. (And I've once implemented it in a situation where PBKDF2 in pure Tcl was so slow the server would disconnect before it was done, but since the client then had the 'ClientKey' stored, further reconnections were fast.) Commented Mar 25 at 6:47
  • 2
    @grawity: The problem of SCRAM is that it has PBKDF2 baked into the protocol. This is a fairly weak algorithm by today's standards (compared to, e.g., Argon2 or bcrypt). Additionally, as the client has to do all the heavy lifting on potentially poor hardware, you further have to limit the cost factor, which makes the password-derived data kept on the server (the StoredKey and `ServerKey) susceptible to brute-force attacks. If the hashing is done server-side, then much stronger algorithms with higher cost factors can be used. Commented Mar 25 at 7:45
  • 2
    @grawity: The only real benefit of SCRAM compared to sending the plaintext password is that the client-side credential doesn’t travel all the way from the client to the target application. So there’s no risk of accidentally logging it at, for example, a reverse proxy. But given the downsides, it’s not really worth it. Commented Mar 25 at 7:45
  • A solid answer, basically summarized with "because it doesn't meaningfully increase security, and gives an extra step where stuff can break [in a bunch of different ways]" Commented Mar 25 at 17:49
  • 1
    Another consideration is that client-side hashing can make updates harder. With server-side hashing, "I" can change the hashing algorithm without the user needing to know, as part of the login process. With client-side hashing, I can change one of the hashing algorithms in that way, but the other needs me to push out a software update and then have everybody run the updater. ... mostly a concern here if the client-side hash turns out to be vulnerable to de-hashing somehow, granted... Commented Mar 26 at 17:24
7

Of course, if the server is compromised, all bets are off

Most threat models consider the web application (and the sysadmins that maintain it) trusted. If it is compromised, hasing passwords client-side does not offer additional protection, as the attacker can modify the web application to disable client-side hashing.

Some applications put less trust in the web application, but then they typically use another authentication method altogether. E.g. ProtonMail uses Secure Remote Password (SRP).

Hashing passwords client-side can still be benificial against accidentally exposing them, for example by writing them to a log. I wrote more about this in my blog post The case for client-side hashing: logging passwords by mistake.

1
  • 8
    The OP asked the question for a case where the client is a desktop application, not a browser (see the last paragraph). So a compromised or malicious server cannot simply disable the hashing. Commented Mar 24 at 8:15

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.