1

I am trying to achieve better security in my authentication system implementation with both server-side hashing and client-side hashing. (See the first reference below for more prerequisite knowledge.)

As I understand it:

  1. Client-side hashing prevents hackers from getting a user's plaintext password and using it for other sites when the server app is compromised. Compared to server-side KDF hashing, it can also help lighten the server load.
  2. Server-side hashing prevents hackers from logging in as users when the server database is compromised.
  3. KDFs such as Argon2 make it expensive for hackers to brute-force a list/dictionary of common or possible plaintext passwords against a hashed password.

I'd like the save some server computing resources. So here comes my question: is it safe to directly hash "a password already hashed with Argon2 on the client-side" on the server-side with SHA-256? Here I mean "safe" by being at least as safe as using server-side only Argon2. Besides, The second reference below also suggests hashing the authentication token (the so-called "validator" in their article) with SHA-256. Is doing this safe?

My answer: an Argon2-hashed password or an authentication token with a length of at least 16 bytes should be safe. The reasons are:

  1. There is no list/dictionary to try since the data is a byte string that can be anything.
  2. A full rainbow table of all 16-byte-long keys should contain 2 ^ 128 entries, which takes at least 2 ^ 128 * 32 B = 2 ^ 133 B ≈ 8 * 10 ^ 39 B = 8 * 10 ^ 27 TB of storage, which is way too big.
  3. Even if we take the peak Bitcoin hash rate till now 170000 Phash/s, it will still take 10 ^ 12 years to enumerate all the possibilities.

However, I am no security expert so I am not sure whether there are any other flaws. So it would be nice if someone professional could share his/her opinion on this.

PS: Here are the related articles and questions I have read and think are useful, and got me into this question.

  1. authentication - Why is client-side hashing of a password so uncommon? - Information Security Stack Exchange
  2. Implementing Secure User Authentication in PHP Applications with Long-Term Persistence (Login with "Remember Me" Cookies) - Paragon Initiative Enterprises Blog
  3. Password Storage - OWASP Cheat Sheet Series
15
  • 2
    Hashing passwords client-side does not make your service more secure. At best, it is a convenience for the user.
    – schroeder
    Sep 1 at 14:10
  • 1
    I'd say as safe as using only Argon2 on the server-side against attacks, those attacks server-side Argon2 protects against. I could find very little information on hashing both on the client-side and the server-side, but this is what I want to do. So I want to make sure it's at least as safe as using only server-side Argon2.
    – Shreck Ye
    Sep 1 at 14:24
  • 1
    Can you edit your question to make that clear?
    – schroeder
    Sep 1 at 14:25
  • 1
    Unfortunately, performance is not a security concern. I think you really need to change the question to what I suggested in order to avoid getting the answers you don't want. And also state what you've stated in the comments, that you want to provide this convenience to the users and that you know that it doesn't improve the security of your service.
    – schroeder
    Sep 2 at 7:16
  • 1
    OP, I agree with you that this could be used to lighten server load. It also provides a (narrow) benefit to users in protecting them to some degree if they use the same password on multiple sites (as argued by the answerer in the link that I referenced in my answer). However, I think there are better ways to achieve these benefits (namely PAKE or SRP), which offer other additional benefits as well. See security.stackexchange.com/questions/242811/… for more info.
    – mti2935
    Sep 2 at 11:03
3

I beg to differ regarding:

Client-side hashing prevents hackers from getting a user's plaintext password and using it for other sites when the server app is compromised.

The client-side scripting that does the Argon2 hashing would have to be served by the server. If the server app is compromised, then the attacker can alter this client-side scripting, so that it captures the user's password before it is hashed, and sends it to the attacker's server.


Questions like this seem to come up from time to time on this board. See the comments (and banter) following the answer at Is it safe to send clear usernames/passwords on a https connection to authenticate users? for one recent example. It seems that there are few (if any) security benefits of client + server hashing as compared with the standard method of sending the plaintext password over https, and salted hashing on the server side; and the use case for client + server hashing is very narrow at best.


Edit 9/3/2021

As requested by OP, I am including two points from our discussion in the comment's following his question (above).

  1. I agree that this could be used to lighten server load. It also provides a (narrow) benefit to users in protecting them to some degree if they use the same password on multiple sites (as argued by the answerer in the link that I referenced above). However, I think there are better ways to achieve these benefits (namely PAKE or SRP), which offer other additional benefits as well. See Alternatives for sending plaintext password while login and Hashing Passphrase in Client-Side JavaScript Rather than Server-Side -- Is it Viable? for more info.

  2. The question of where the salt (as needed for the client-side Argon2 hashing) would be stored must be addressed. If on the server, then some protocol would need to be worked out for the client first getting the salt for the user from the server (and at that point, this begins to look very much like PAKE/SRP), then proceeding with the client-side hashing, and finally authenticating with the server. If on the client, then this presents usability issues with clients logging in from different browsers/devices, etc.

Last but not least - Any of the implementations discussed in the answers and comments following this question would involve client-side (on 'in-browser') crypto - meaning that the server would need to serve-up client-side (e.g. javascript) crypto code. If we don't trust the server with our plaintext password, then how can we trust the server to serve secure crypto code? A rogue server admin (or an attacker that has gained access to the server) could modify the crypto code that the server serves, and capture the user's plaintext password, as I alluded to at the beginning of this answer. This is the infamous browser crypto chicken-and-egg problem. I realize that this is tangential to the question - but if/when we have a solution to this problem that is well-adopted (or if/when browsers implement a protocol like PAKE or SRP natively), then there might actually be tangible security benefits to a solution like one the above discussed here (over the standard method of sending the plaintext password over https), in addition to the other benefits mentioned.

2

So here comes my question: is it safe to directly hash "a password already hashed with Argon2 on the client-side" on the server-side with SHA-256? Here I mean "safe" by it's at least as safe as using server-side only Argon2.

Leaving aside any possible concerns with the cryptography, there are a couple of important other questions that you need to consider:

  • Is the client-side library you're using for Argon2id as robust as the server-side implementation?
  • Can you guarantee the integrity of this library when it's used (i.e, that an attacker can't tamper with through a MitM attack, XSS vuln, etc)?
  • Can you create an equally strong hash on the client-side (based on the parameters you configure the algorithm with) as you can server-side without an unacceptable performance impact on the client? How about for mobile devices?
3
  • Here are my answers: * I am going to use the "libsodium.js" lib which is maintained by "libsodium"'s authors so I think it should be robust enough. * If an attacker can change the JavaScript code then he/she can send the plaintext password directly to his/her server, so in this case, server-side Argon2 doesn't help either. * Mobile devices are getting more and more powerful these days with octa-core CPUs so I think they can handle. On the contrary, if I use server-side only Argon2 and when there are too many login requests, it can affect the throughput of the server.
    – Shreck Ye
    Sep 2 at 7:42
  • Have you actually tested how well the libsodium.js library performs on mobile devices (with your defined parameters) on a midrange phone that's a few years old? They're certainly more powerful than they used to be, but algorithms like Argon2id are intentionally designed to be slow (and should be memory-heavy, although you can tune that).
    – Gh0stFish
    Sep 2 at 8:36
  • Yeah, the parameters can be tuned. there is an "INTERACTIVE" version that's designed for such online operations.
    – Shreck Ye
    Sep 2 at 8:48
-2

Hashing a password on client side is 100% completely worthless.

  • If server app is compromised they can simply comment out / rewrite the javascript code to hash it client side.
  • clientside javascript should not be trusted! Ever! For any reason!
  • If you hash a 30+ character password to 16 bytes, you are actively decreasing the security of the user.
  • Hashing a password clientside effectively makes the clientside hash the real password. If its stolen, they dont need the pre-hashed version for anything.

The correct way of doing this is:

3
  • Not worthless. It's a convenience for the user in case of certain client-side threats. It has little to no impact on the security of the service, however.
    – schroeder
    Sep 2 at 7:19
  • @schroeder How is it not worthless? What am I missing? It does not protect anything if the client side computer gets attacked. It does not protect anything if the clientside javascript gets attacked. It does not protect anything if the serverside gets attacked. It does not protect anything if their is a man in the middle attack. Sep 2 at 14:23
  • @CaffeineAddiction I agree with you that we are not quite there yet with client-side (or 'in-browser') crypto. But, we are inching our way there - we now have CSPRNG in javascript, subresource integrity, native crypto functions in the web crypto API, etc. ProtonMail is showing that it can be done. If the user's device is compromised, then it's game over no matter what - even if it's done the 'correct way' as per your answer. But, I agree with you about a server attack. That's why I think it's time we had a way to verify the integrity of client-side code, as I mention in my answer.
    – mti2935
    Sep 3 at 13:30

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.