0
$\begingroup$

I am writing a webpage on which a user can set (when registering) and change his/her password. This page is only served over HTTPS. When the user is altering his/her password, he/she is already authenticated with his/her current password.

I am planning to use bcrypt to encrypt and salt the password. Should this be done client-side (browser) or at the server? In case of client-side, I could verify on the server that bcrypt was applied with at least X (for example, 12) iterations.

$\endgroup$
0
$\begingroup$

Normally password hashing is done server side. It doesn't need to be, but if you use client-side password stretching there are a few things to be concerned about.

  • Your web clients will need to have javascript enabled. People may disable it for security, privacy or accessibility reasons. Or because noscript browsing is more convenient. Or because they want to reduce battery drain.
  • Resource limitations - If the client doesn't have a high end computer then advanced password hashing might take longer than expected. Maybe their CPU is much slower than a desktop, laptop, or server. Maybe they don't have much RAM available. (For memory-hard password hash algorithms, which bcrypt isn't.)
  • Attacker/Defender Efficiency - Given the option to do server-side hashing with a native optimized implementation of a hash function or a client-side implementation in javascript the serverside implementation will be more efficient. Consider an algorithm running both client-side and server-side with the same parameters. Both do the same amount of work, but the server will finish first. To meet user demands for responsiveness you will lower the cost parameters of client-side hashing, compared to the cost parameters for server-side. This makes an offline password cracker's job easier if your password database does get breached.
  • If you do client-side password hashing then the salt needs to be known to anyone attempting to log in. This would allow attackers to use a pre-computation based attack against specific targets if they could anticipate stealing password hashes from the server.

Some people also add "A password hash is password-equivalent so client side hashing adds no security at all." This is good advice for people that think password hashing is an alternative to authenticated encryption (it isn't) but often people mistakenly use it as an argument to not do client side password-stretching. It's a bad argument because client- or server-side, you're still vulnerable to eavesdropping if you don't use encryption


One major advantage to client-side hashing is that it takes the burden of password stretching away from the server. To validate passwords the server needs to stretch every password submission it gets. (Ignoring successful brute-force prevention.) This could be exploited for a denial of service attack.

For desktop applications I might recommend client side hashing. (With end-to-end encryption with pinned server keys.)

If you use client-side password stretching then you SHOULD NOT store raw password hashes in your database. This would let anyone who stole a password-hash database to log in as any user without determining what their password was. Instead store a hash of the hash. As in something like $\text{SHA-512}(\text{bccrypt}(password, salt))$.

(Bcrypt would be done client-side and SHA-2 server-side. Normally plain-old-hashing of passwords is not secure, but hashing the hash is okay because the input has already been salted and stretched. People won't bypass the bcrypt part and go directly to searching for SHA-2 preimages because finding preimages would be a waste of time compared to guessing and hashing passwords.)


You said:

I could verify on the server that bcrypt was applied with at least X (for example, 12) iterations.

Which leads me to believe you're misunderstanding something. It sounds like you want to hash both client-side and server-side.

First, that isn't possible unless you also have the original (plaintext) password. Second, that is a waste of effort, assuming you're testing $H(p) == H(p)$ not $H(H(p)) == \text{StoredHash}$. (The latter being splitting hash-stretching work between client and server. The former being redundant because both client and server do the same work.)

To verify that enough bcrypt rounds were used you only need to password-hash (using bcrypt) each new password once. Just hash the plaintext password server-side whenever an account's password is changed with the appropriate parameters. Then save that hash to use for password verification from that point on, for the remaining lifespan of that password.


Lastly, I think I should recommend against client-side hashing. Not because it's inherently bad, but because I think server-side hashing will be safer and easier to implement.

You also should be using argon2, which is designed to be costly by requiring both CPU and RAM resources. bcrypt requires small amounts of RAM and it's cost parameter is only used to alter CPU time. argon2 was the winner of the password hashing competition.

If you're using PHP consider using the built-in password hashing API, whether you use bcrypt or argon2. It supports both algorithms, takes care of salt generation for you, and is a very simple API.

$\endgroup$
  • $\begingroup$ Whaa, this was very informational. Thank you so much. $\endgroup$ – Jessica Sep 13 '18 at 13:19

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.