9

This question already has an answer here:

I am currently implementing a simple login system. I would like to securely store all the user passwords in my database. The passwords are the most important components. All the other user data is not important. If an account gets hacked, as long as the password cannot be figured out its fine; other data are things like: score and account creation date.

I am assuming that some of my users will be using the same password as they are for other sites. So i need to make sure that the password cannot be read by a potential hacker.

Can I generate the Hash on the client side somehow to avoid any hacker ever seeing the real password?

Before you say I shouldn't do it on the client. The server has a constant game loop that needs to run, and I don't want the game to lag every time someone registers an account or tries to login. This approach would take some of the load off the server.

EDIT: Since this question was closed I would like to elaborate. Moving to the client allows me to use a lot more rounds when hashing the password, since this wont affect the server or other users experience in any way. This also makes it much easier to prevent a user from spamming requests and slowing down the server.

marked as duplicate by crovers, Steve, Xander, Anders, techraf Jan 18 '17 at 9:39

This question has been asked before and already has an answer. If those answers do not fully address your question, please ask a new question.

  • 14
    If login requests cause your game loop to lag, then you should handle those on a separate thread. Hashing on the client side is generally not a good idea. See: security.stackexchange.com/q/8596/29865 – Ajedi32 Jan 17 '17 at 14:22
  • I am limited to a single core server. And like I have said, if an account gets hacked that doesn't matter. The only thing tht matters is if a password can be turned into plain-text because it can be used on other sites like: paypal or facebook. – Sidney de Vries Jan 17 '17 at 14:28
  • 3
    You would still need to hash on the server side if you hashed on the client - else you'll have a bunch of password equivalent strings sitting in your database, which, if stolen, could probably be brute-forced anyway to reveal the password (since you can't control the client specifications, you'd probably have to err on the side of a relatively quick hash). If timing is critical, you could have an authentication server, which is distinct from the game server - yes, it increases cost, but allows for easier scaling later... – Matthew Jan 17 '17 at 14:44
  • 3
    Just a note, you can still do multithreading on a single core. It won't necessarily be faster, but the benefit is it likely won't block for the entire duration of the hash computation. Rather it would probably switch threads during the hash computation depending on how the code is implemented (what language) and how long it takes (for example hundreds of ms with bcrypt). – Steve Jan 18 '17 at 4:03
  • 1
    I'm voting to re-open this question, because unlike the linked duplicate, this question is specifically asking in relation to server load issues / server relief. – Bryan Field Jan 18 '17 at 15:06
20

I am assuming that some of my users will be using the same password as they are for other sites.

In the context of protecting password from use on other sites (besides your own), it does not matter whether you hash the password client-side or server-side.

However, depending on your client environment, (i.e. web browsers) you may have difficulty finding a sufficiently well-optimized (strong) implementation of a Slow Password Hash. (e.g. Argon2 or BCrypt) So, your suggestion of client-side hashing may only be practical in App environments where native cryptography is available.

Of course, hashing client-side will do nothing to protect the password from being used on your site. An attacker who has stolen the hash can simply skip the client-side hash routine and become authenticated with your server. (never trust the client) Fortunately, this attack vector can be solved server-side. (see below)

The server has a constant game loop that needs to run, and I don't want the game to lag every time someone registers an account or tries to login. This approach would take some of the load off the server.

As you know, it is traditionally recommended to hash sever-side. If you are concerned about load, I submit a few suggestions:

  1. The Work Factor should be tuned only to take ~100ms on your target hardware. (your server) So, that's not really much of a lag, is it? I normally advocate 50ms-500ms, but your site could use a lower value and still be considered robust.

  2. The Hash should be in a separate thread from your main game loop (async) so that the game can continue to operate (albeit with slightly poorer performance) while the hash is calculating.

  3. You should include some form of DoS protection to limit the amount of hashes your server would calculate. Typically, the site would throttle sign-in attempts (per user?) so that an attacker would not be able brute-force effectively. Such a throttle would prevent endless hash computation.

  4. If you like, you could hash the password on both the Client and the Server, if the client environment has sufficient native support. (described above) Then you can get away with an even smaller work factor server-side.

    If the client-side has a sufficiently strong Work Factor, then it actually becomes reasonable to run only a quick (i.e. zero lag) SHA-2 on the server-side, and that will solve your problem completely. (thanks @HenningMakholm)

Finally, make sure the client sends the password (or hash) over HTTPS only. This is the most important thing, as no amount of supposed hashing will protect passwords typed into an HTTP site.

  • 2
    100 ms is a lot in the context of a game I think. Also is HTTPS a must if I already hashed the password before I send it? Why is that the case since it would already be hashed – Sidney de Vries Jan 17 '17 at 14:35
  • 3
    In a web browser environment, HTTP tampering can be used to disable all client-side hashing. Even if it weren't, theft of a hash is pretty serious, especially if it was for a sensitive service (e.g. Facebook, Google, PayPal, Online Banking) as you suggest is possible. – Bryan Field Jan 17 '17 at 14:39
  • 2
    As for lag, I think that #2 (async) is your best option, but you can also tune the work factor to be lower as suggested in #1, and consider #4 for additional security. – Bryan Field Jan 17 '17 at 14:42
  • 2
    Depending on the technology you use, you might be able to set (operating system) priorities for your different threads. You would give the "login" thread a low and the "game" thread a high priority. Then the operating system will make sure that if load goes high, the game keeps running smoothly and the login has to wait a bit (which ought to be fine). – 5gon12eder Jan 17 '17 at 17:17
  • 3
    For option 4 it would be secure to reduce the server work to a single SHA-256 (or even MD5 if you're really desperate for CPU cycles), right? – Henning Makholm Jan 17 '17 at 20:43
5

If the goal is only to protect the users from password-reuse when your database is compromised, then yes, you can move the hashing to the client-side.

You need to accept that this is insufficient in general for actually securing your application. It is insufficient because, in general, most such approaches have one of two faults:

  1. They transmit a token which is "password-like," in the sense that if I do break your assumptions about HTTPS or whatever, I get to log in as you.

  2. They store a token which is "password-like," in the sense that if I do get to look at a row in the users table, I get to log in as that user.

The first one is just any client-side manipulation which ends with "then I transmit to the server, the server hashes it, and the server compares if the hash is equal to something in the database." The second one is characteristic of challenge-response schemes, "I send you this string, you encrypt it with your key, you send back the encrypted version, and I see if I can decrypt it to get the original challenge again."

I chose the words for that second case very carefully because it reveals that actually, you can avoid these two if your system is based on asymmetric crypto, public keys and private keys. Your server can store a public key if your client can compute a private key. This used to be completely infeasible with RSA etc. ("Oh, I'll just use the password as a seed for a deterministic random number generator and do key setup each time" -- are you joking?) but has become more possible with elliptic curve cryptography, where the private key can often literally just be an arbitrary bitstring of a certain length. You use a key-derivation function to get that, and you're safe.

However, you might not want to "roll your own" crypto in any case: in that case, look for an implementation of the Secure Remote Password (SRP) protocol created at Stanford; it has seen a lot of cryptanalysis and has been around for a while and likely has implementations that you can find and use.

3

You can do the expensive part of password hashing on the client side, but you need to exercise care. For example, the following protocol would be wrong:

  1. Client establishes an authenticated, confidential channel to the server. (E.g., an SSL connection with a valid server certificate.)
  2. Client sends username to server
  3. Server looks up the corresponding password entry (username, salt, hashed_pwd)
  4. Server sends salt to the client.
  5. Client hashes their password with a costly password hash: client_hash = pwhash(salt, pwd)
  6. Client sends client_hash to the server.
  7. Server compares client_hash to hashed_pwd; the user is authenticated if they are equal.

That protocol would be insecure because the hashes that the server stores would be "password-equivalent"; a hacker that stole the server's password database would be able to replay the hashes over the protocol to impersonate any users. So the server still needs to do salted hashing; what it can do is move the costliest computations to the client so that the server only needs to compute a fast salted hash.

A better protocol would be this:

  1. Client establishes an authenticated, confidential channel to the server. (E.g., an SSL connection with a valid server certificate.)
  2. Client sends username to server
  3. Server looks up the corresponding password entry (username, client_salt, server_salt, hashed_pwd). (This is a changed step compared to the bad protocol: the good protocol has two salts, one for the client to use, one for the server.)
  4. Server sends client_salt to the client.
  5. Client hashes their password with a costly password hash: client_hash = pwhash(client_salt, pwd)
  6. Client sends client_hash to the server.
  7. Server computes server_hash = fast_salted_hash(server_salt, client_hash). The fast_salted_hash function could be HMAC or Blake2, keyed with the server_salt. (This is an additional step that did not exist in the bad protocol.)
  8. Server compares server_hash to hashed_pwd; the user is authenticated if they are equal.

By salting the password hash at the client side, it means that users who choose the same password will send different hashes over the wire, so that a hacker who compromises one client and learns its client_hash cannot replay it to log in as a user with the same password.

By only storing a salted hash of the client_hash, we defend against attackers who steal the password database, just like in conventional password hashing.

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