3

Say I have a client and a server. I want the user with the client to login to the server with a username and password combination.

To login, they have to enter the right password which (when hashed) matches the details stored on my server.

Where do I perform the hashing function on the password entered by the user? Should the client's computer perform the hashing function and send the hashed result to my server (hoping that nobody figures out a working hash to send)? Or, should the server perform the hash on the password (hoping the connection between the two is secure)?

I'm keeping this as abstract as possible in hopes of applying it to multiple situations.

Erikster
  • 43
  • 1
  • 5
  • This, to a certain degree, is a duplicate of [Why is client-side hashing of a password so uncommon?](https://security.stackexchange.com/questions/53594/why-is-client-side-hashing-of-a-password-so-uncommon). – Karol Babioch Mar 25 '14 at 15:15
  • The answer of this question [Why is it not standard practice to run password-based KDF's client-side](http://security.stackexchange.com/q/53833/8343) also explains pros and cons of client side hashing. – martinstoeckli Mar 26 '14 at 14:36

4 Answers4

10

Ideally, we should do most (but not all !) of the hashing on the client side.

The overall need for password hashing, with all the involved iterations and salts (see this answer), is to make sure that the value which is stored (the "password verification token") cannot easily be used for an offline dictionary attack (the attacker tries potential password, until one which matches the stored value is found). For that specific functionality to be fulfilled, it suffices that the slow-and-salted password hashing occurs "somewhere", and the client machine is a reasonable place for that.

However, protection against offline dictionary attacks is only one part of the security goal. In particular, we do not want an attacker to be able to obtain "password-equivalent" values. If you do the complete hashing client-side, and store the value "as is" on the server, then a glimpse on the server's database (as happens all too often with SQL injections and lost backup tapes) will reveal the hash values and allow the attacker to log in as any user immediately. Thus, you still want to do some hashing server side.

What works, for instance, is to have the slow-and-salted hash on the client, resulting in a value V, and the server stores SHA-256(V). Since the slow-and-salted hash has been done, the protection against dictionary attacks is there; and since the server does not store V but still requires the client to send V to be granted access, read-only breaches are not trivially escalated into full read-write compromise.

Client-side hashing has the very interesting feature of using client-side resources, not server-side, thus allowing a given server to process many concurrent clients without running out of CPU, while still having an overall slow-and-salted password hashing. However, this is not often done in practice because of some drawbacks:

  • The client cannot hash without knowing the salt, which is stored on the server. The protocol thus implies an extra roundtrip: the client must send the username, the server responds with the salt, and then (only then) can the client do the slow-and-salted hash.

  • Clients are heterogeneous: some can be quite feeble, which implies a hard limit on the number of iterations that can be applied (because a slow smartphone as client does not mean that the human user is more patient). This is especially true for Web-based clients, because Javascript computations are awfully slow (when compared to native code or even Java or Silverlight applets).

  • Client-side hashing means client-side code, which may not be easily upgraded or modified. With server-side hashing, the hash function choice is entirely up to the server and can be switched without having to alter installed clients in any way.

  • Some servers like to have access to cleartext passwords occasionally, not for storage, but for other features such as automatically detecting very poor user passwords, or sending a copy of the passwords to the relevant Law enforcement authorities (where applicable -- I am not saying that this is good or bad, but when it applies, it is not subject to a choice by whoever designs the login system; if you are in a country where you must allow governmental agencies to peek at passwords, then, well, you must, so this becomes an element of the context to be dealt with).

Thomas Pornin
  • 322,884
  • 58
  • 787
  • 955
  • a much better answer than the accepted answer. – NH. Aug 05 '17 at 16:36
  • Surely if the database is compromised and the SHA256(V) is compromised, then accounts are effectively protected only by the SHA256, which can be brute-forced much easier than is acceptable (and the length of the input may be pre-known, as V is calculated on the client side, so the algorithm is exposed even to SQL injections) – Hack5 Sep 15 '20 at 18:33
4

Think about the purpose of hashing.

If someone breaks into your database they will not find passwords but only hashes which need to be brute forced into passwords in order to be useful.

By allowing the client to send a hash instead of the password you effectively make the hash the new password and all benefits are lost. i.e. if someone gets into the database they can instantly log into all accounts with the data they find there.

Therefor the client should always send the password and the server should do the hashing.

user2675345
  • 1,651
  • 9
  • 10
  • This makes sense. The point of hashing is to prevent people from stealing passwords in the DB so they can't login with them, but if they can just steal and send the hashed password then hashing was just a waste of computational time. – Erikster Mar 25 '14 at 15:25
  • I think you're skipping a step though. Allowing the client to hash the password doesn't preclude the server from doing it too. – Brendan Long Mar 25 '14 at 19:45
  • The client hashes the password and does what with it? The server still needs to know the password one way or another to assert that the hash is correct. – user2675345 Mar 26 '14 at 09:08
  • Yes, but the server can hash whatever value it gets (the pre-hashed password) before storing it. Just because the client hashed the password doesn't mean the server can't too. – Brendan Long Mar 26 '14 at 15:16
4

Passwords should be hashed at least once on the server, to prevent pass-the-hash style attacks where a malicious attacker can simply inject the hash he sniffed from the network to authenticate. This doesn't however mean that you shouldn't hash the password locally as well. A fairly paranoid strategy is to have the user submit an iterated hash of a password, using a large number of iterations, say 10,000, which you then store.

When the user wants to authenticate they enter the same password, but only hashed 9999 times. The server then locally hashes the 9999-times hashed password once, and if it matches the stored 10,000-times hashed password the server replaces the stored hash with the 9999-times iterated hash. On each subsequent attempt the user iterates the hash one fewer times, and if authenticated successfully the server replaces the old hash with the new hash.

The user is forced to reset his password when the number of iterations becomes low. Various tactics can be used to incorporate salts at various points to add additional security. The server should also hold the number of iterations it expects the client to have submitted so that the client (or multiple clients) can be kept in sync.

In summary, always hash the password on the server, but that doesn't prevent you from hashing locally also, which can also prevent the server from ever knowing the user's password, even briefly.

jhoyla
  • 439
  • 2
  • 6
  • Do you have a source explaining how this iterative hashing strategy works? Or the name of it so I can look it up? It's not really adding up in my head. – user2675345 Mar 25 '14 at 15:35
  • 1
    In what situation does this improve security? The cases I can think of are: (1) attacker has the plaintext password -> you lose (2) attacker has a hashed password and tries to log in with it -> server rejects it because it wants the plaintext (3) attacker has a hashed password and tries to track it -> re-hashing it on the server side doesn't make this any harder since the attacker only needs one valid password hash. – Brendan Long Mar 25 '14 at 19:44
  • The goal with this strategy is to assure the client that the server, whether maliciously or accidentally, cannot leak his passwords, even if the server is always malicious. The client can guarantee the server cannot leak his password, because the server never sees it, not even at account creation. The reducing hash means that an attacker cannot just pass the hash, because each hash is only good once. The salting scheme is necessary to prevent the attacker from forging a login page with a low hash number to obtain a large hash chain. I saw this in a paper I read years ago, I'll look for it. – jhoyla Mar 25 '14 at 21:22
  • "have the user submit an iterated hash of a password, using a large number of iterations, say 10,000, which you then store.", "When the user wants to authenticate they enter the same password, but only hashed 9999 times." "The server then locally hashes the password once," In this last step how does the server know what the password and how can it get the 9999th hash iteration if all you have provided is the 10000th hash iteration? I don't like downvoting but this is either very poorly explained or completely made up. – user2675345 Mar 26 '14 at 09:12
  • 1
    On account creation the server is given the 10000 times hashed password. On the subsequent login the server is given the 9,999 times hashed password, which it then hashes once to check it is correct. If it matches the 10,000 times hashed password then the server authenticates the client. The server then replaces it's copy of the 10,000 times hashed password with the 9,999 times password which it was sent by the user. It never reverses the hash, it simply checks if the user supplied hash matches. *insert unnecessary disparaging comment here* – jhoyla Mar 26 '14 at 14:06
  • I'm not arguing against double hashing. I'm saying lowering the iterations every time doesn't help at all (because the password is still the same, and the attacker only needs one hash to break it). – Brendan Long Mar 26 '14 at 15:18
  • The reducing hash means the attacker can't just capture the hash off the wire and pass that to the server. He has to break the password. In this case the iteration is not for increasing the attackers work, but for preventing replay attacks. The first hash in the chain should probably be something slow, and the remainder of the hashes fast. The beauty of the scheme is that the client can use whichever hash fiction he wishes initially, as long as the last 10,000 iterations are what the server expects. – jhoyla Mar 26 '14 at 16:31
  • @jhoyla Ah I see. It seems easier to just hash it one more time on the server side. – Brendan Long Mar 26 '14 at 18:53
  • That works too, it gives fewer options for salting from what I recall. – jhoyla Mar 26 '14 at 20:39
0

Should the client's computer perform the hashing function and send the hashed result to my server (hoping that nobody figures out a working hash to send)?

If you use firefox, I highly suggest you download a simple little tool called "TamperData." Turn it on and see what kinds of havoc you can play if you sit in between the client and the server.

So the answer to this question is: NO. The best way to handle this is to store a salted hash on your database and your server application treats all incoming data as malicious. In your case, how would you implement the client hash? In Javascript? In order to verify the hash, server and client code must be in lock-step, and since the attacker has full access to your Javascript hashing algorithm... you've actually made an awful lot of implementation information public, such as which hash you're using, you'd have to give away your salt too--just far too much to trust to chance. Better that your pages be simple and dumb, and leave the validation for the server application. Usability might require some client-side validation, just be careful here because again, you're giving away implementation details to your possible attackers.

In short:

  1. Assume all source (client/server/database) is public knowledge and freely available. A sub-point here is to also remember that inlcuding Javascript libraries in your pages opens you up to attack. Better to have local copies in your organization that have been professionally vetted. Watch out for webservices too! Just because a fortune-500 exposes a webservice doesn't mean that an attacker can't leverage their input/output against you!

  2. Find your boundaries--in MVC applications like web applications this is pretty easy. Anything coming from the user is treated as hostile until validated.

avgvstvs
  • 940
  • 1
  • 7
  • 19
  • Making the hash function and salt public isn't a big deal though. If you know what you're doing, the hash function will be obvious (PBKDF2, bcrypt or scrypt) and the salt is just a random number which is useless by itself. – Brendan Long Mar 25 '14 at 19:40
  • How would the hash be obvious to an attacker that only has access to the HTML src? – avgvstvs Mar 26 '14 at 15:12
  • It doesn't matter. The hash function can be public and it won't hurt your security at all. – Brendan Long Mar 26 '14 at 15:18
  • My point was purely about hiding your application's implementation. We're talking about passwords. If your client code contains both information about which hash you're using, and the salt, you just radically decreased the difficulty of building your rainbow tables. Hashcat for the win. – avgvstvs Mar 26 '14 at 16:28
  • And since I'm on my soapbox, what JavaScript PRNG would you use? Further, since its JavaScript as an attacker there are means by which you can subvert a site-user's browser session and redefine any functions you want. No, the more I think about it, I will never agree with exposing hash + salt code on the client and leaving the rest up to chance. How does the server know that the salt is valid and not tampered with? How does it know the salt was actually random? – avgvstvs Mar 26 '14 at 16:34
  • And how would that work even? You generate a random number and a hash in the browser, pass it to the server. How are you going to validate the password stored in the database if you're computing a random salt on the client every time? – avgvstvs Mar 26 '14 at 16:43
  • Hey don't turn this into a strawman. My argument was specific: There's no reason to hide the hash function or salt. I agree with you that JavaScript cryptography is pointless, but the question was about client-side hashing in general, and not all clients are JavaScript. – Brendan Long Mar 26 '14 at 17:26
  • The way to make it work is this: The client sends the server the user name, the server responds with a salt and number of iterations / difficulty. The client hashes the password given that information and sends it to the server. The server then hashes it one more time, then validates it against what it has stored. I'm not claiming there's no downsides, they are: (1) the server gives away the fact that the user name is valid (most websites do this anyway though) (2) there's an extra round-trip to request the salt and (3) this doesn't work for all clients (pointless in JavaScript for example). – Brendan Long Mar 26 '14 at 17:29