1

In a web-based auth system I do the following:

  1. Client: Ask the server for a "nonce".
  2. Client: Generate a "cnonce".
  3. Client: hash(nonce + cnonce + password)
  4. Client: Send cnonce and the hash from point 3 to the server.

  5. Server: hash(nonce + cnonce + password)

  6. Server: Compare the hashes.

This would require me to save passwords in plaintext/encrypted as the server needs "password" to calculate the hash to compare with.

Of course I don't want to do this and therefore I hash all passwords saved on the server with hash(password, salt). But now I can't compare with the hash the client sends me as I don't have the password component.

One way to solve this would be to provide the client with the unique user salt so that the client could calculate hash(nonce + cnonce + hash(password, salt)) and send it to the server, allowing the server to compare the hashes. But according to How to store salt? the salt should never be shared.

The reason to why I use nonce on a HTTPS connection is described in: Should I hash the password before sending it to the server side?

So my question is how to get out of this infinite loop of "don'ts"?

  • I don't see the improved security compared to calculating the hash of the password and sending that to the server. Why the nonces? Which attack are you mitigating by using them? – Silver Jan 6 '17 at 15:28
  • Well, the hashing question gives advice which you probably wouldn't get here: it's effectively creating a password equivalent, using code which has been transferred over a method which it considers insecure. Either you trust that the HTTPS stream hasn't been modified or intercepted, in which case the password can be sent, or you don't, in which case the code to hash on client side is suspect too... – Matthew Jan 6 '17 at 15:30
  • @Matthew, I agree. I don't agree to the accepted answer to the referred question either. Gurgy, how sensitive is your application? The entire world is simply relying on HTTPS so unless you are trying to secure something really sensitive, just go with HTTPS and send passwords. Much more important is the SSL configuration. Check ssllabs.com/ssltest on your website. – Silver Jan 6 '17 at 15:33
  • @Matthew Thats a valid point – Gurgy Jan 6 '17 at 15:35
  • 1
    That pretty much settles it, "Just trust HTTPS, if you don't there is no saving you anyway". – Gurgy Jan 6 '17 at 15:43
2

To summarise the comments:

I don't fully understand the stackoverflow answer you are referring to. In your explanation you send a nonce from the server to the client over HTTPS which is considered broken in this attack. Therefor the attacker obtains the nonce. A cnonce is generated and both nonces and the password are hashed.

The cnonce and the hash are sent over the (presumably insecure) HTTPS connection. The attacker now has the nonce, cnonce and hash. He can try to reverse the hash to obtain the password (bruteforce). But that is not even necessary as the server is willing to accept the hash and grant access. The attack simply resends the hash. As @Matthew stated, you've just created a password substitute.

You could invalidate the server nonce after one authentication so that if the hash is resent it is rejected. This seems like a security improvement since the attacker could only succeed by reversing the hash which is very hard with complex and long passwords.

Does the effort outweigh the benefit? I think so, HTTPS is considered secure if configured correctly. The entire internet is depending on HTTPS to secure authentication so unless your application is very sensitive, you should rely on it.

Good luck on the assignment!

0

The objective of the salt is that different accounts will have different hashed passwords (by which I refer to the end result that you store in your db, after all transformations).

Thus, it is not a problem that an attacker learns the salt used for a given user. He could start calculating possible hashed passwords, but he would need a different rainbow table for user, so it wouldn't be useful to engage in such calculations before stealing your database.

The problem with sharing the salt with the client is that you may be leaking the users that exists on your db, as existing users will always return the same salt (you can obfuscate this by returning a salt derived from the given username for users that don't really exist).

Also note that, although you don't specify what exactly is the + operation in your hashes above, you should replace with hmac the steps of taking a hash of the combined values.

For instance, supposing that + was the concatenation operator, a malicious actor that intercepted a login request with nonce=123456789 and cnonce=987654321, could trivially impersonate a login request where the server provided a nonce=12345 (ie. there is an higher probability of getting a valid nonce that just getting exactly the same one).

You may also find interesting the Secure Remote Password protocol (RFC 5054). Although both parties work with plaintext passwords, you could equally adapt it to salted hashes calculated at both sides (your "password" is the output of the salt operation).

Finally, you have to understand that in these schemas, the salted hash is plaintext-equivalent, as that's enough for logging in. You could store that salted password encrypted¹ with an application-specific password (it would have a role similar to a pepper), but nonetheless its properties in case of compromise would be a bit different than in the usual approach. If someone stole your database (eg. from a sql dump in an unprotected backup), this pepper -if not stolen with the db- would be the only thing in the way of the attacker preventing him from impersonating any user.

¹ yes, it needs to be reversible for your protocol to work

0

The server certainly needs to store a verifier for every password entry. Our verifiers will be (user_salt, server_salt, server_hash) triples, where:

  • user_salt and server_salt are random 16 byte arrays.
  • server_hash = keyedhash(server_salt, pwhash(user_salt, password)), where:
    • pwhash denotes a "slow" password hashing function like PBKDF2, bcrypt, scrypt or Argon2;
    • keyedhash denotes a keyed hash function like HMAC-SHA2.

Now we can use an authentication protocol like this:

  1. Client: Sends username to server;
  2. Server: Responds with the corresponding user_salt;
  3. Client: Computes client_hash = pwhash(user_salt, password);
  4. Client: Sends client_hash to server;
  5. Server: Computes server_hash = keyedhash(server_salt, client_hash);
  6. Compares server_hash to the value stored in the user's password entry.

The enrollment/password change protocol is similar:

  1. Client: authenticates with the server;
  2. Client: sends a password change request;
  3. Server: responds with a random new_client_salt;
  4. Client: computes new_client_hash = pwhash(new_client_salt, new_password);
  5. Client: sends new_client_hash;
  6. Server: selects a random new_server_salt;
  7. Server: computes new_server_hash = keyedhash(new_server_salt, new_client_hash);
  8. Server: stores (new_client_salt, new_server_salt, new_server_hash) as username's new password entry.

Both protocols should be executed over an authenticated, confidential channel (e.g., an SSL connection). An eavesdropper who sees client_hash would be able to impersonate the user.

One of the advantages of a protocol like this is that, in addition to never sending the password over the channel, it computes the slow password hashing function on the client side, which means you can crank up the function's work factor to make password cracking attacks harder.

See also these links for similar proposals:

  • Computing the hash on the client can be a risk - you can tune for your server performance, but not your client. There are FPGA implementations of bcrypt, which keep getting faster. You can mitigate that with an artificial rate limit on the server side - only allowing one login attempt per second, for example. – Matthew Jan 6 '17 at 19:56
  • What if we assume TLS does not protect confidentiality? That is the attack scenario the OP is trying to protect against. The client_hash is leaked and you have not mitigated the previously described attack scenario since client_hash has just become a password substitute. To improve this, you could invalidate the salt after one submission so that the attacker should respond faster than the real client. This would require him to break TLS in record speed and would improve security. (comment too long) – Silver Jan 9 '17 at 9:24
  • (continued) You've probably implied that the user_salt is invalidated after the client submitted his client_hash but I wanted to make it explicit. – Silver Jan 9 '17 at 9:24

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.