If a site uses HTTP, then hashing the password client-side and sending it up to the server is equivalent to sending a clear text password. If an attacker can already read your traffic, what is stopping them from using your password's hash to log-in to your account?
It stops a compromised server from silently leaking unhashed passwords.
It makes password hashing user auditable.
You could even do a call and response model to stop the hashed password to log in at all. Here is a primitive scheme for such a model (public key crypto probably enables more clever schemes, not sure):
- Upon signup, generate hashes of "$password$site$i" for i in 1 to 1000. Send these to the server and have the server hash them again.
- Upon login, after the user has entered their password into the box, send an integer from i from 1 to 1000 to the browser, have the browser send back the hash of "$password$site$i".
Now a compromised hash can only let you log in 1 time in 1000. Combine that fact with the other available signals for "is this who we think it is" and you should be able to reject people who stole the hash reasonably reliably. Meanwhile since you are still hashing the password on the server (again) you have lost literally nothing but a tiny bit of computation time.
There's nothing stopping you from hashing your own passwords client side and sending your bcrypt hash up to the server except some sites still truncate the passwords to 32/16 chars etc.
When you have the need for the level of security, client side hashing will not be as good as dedicated HSMs that many services now use on authentication.
Writing your own crypto flows can be extremely dangerous as you open yourself to all kinds of side channel attacks.
As for writing my own crypto. Indeed, if anyone actually used the scheme I suggested they would be making a mistake. I wrote it not to be used but to demonstrate that we can do better in an easy to understand way. Unlike me, Google has the resources to read the papers, do the math, carefully implement this, and do it properly.
Keywords for how to do it properly include "zero knowledge password proof" and "password authenticate key exchange".
PS. It's irrelevant to this conversation, but putting all my passwords into one program has always struck me as a monumentally stupid idea. I use one for passwords I don't care about, I memorize unique passwords for passwords I do care about.
If you trust the site to deploy correct JavaScript to do this, then that's the same level of trust that they implemented password salting and hashing server side. You don't gain any robustness by moving this to JavaScript.
Your scheme is just a weak salting technique. You'd be better off with just using a longer salt and hash function.
I can trust the site to deploy the correct javascript more than I can trust it not to steal passwords because
- That is auditable - it is impossible for a malicious site to do so without risking being caught.
- The HTML/JS can be served from static cloud storage that is far less likely to be hacked than the server running a DB verifying passwords.
Hardly. Minimization and obfuscation is trivial, and you can ensure the output is always different in order to defeat auditing. Not great for caching obviously, but 'auditability' is not achievable if the server is determined to fool you.
> - The HTML/JS can be served from static cloud storage that is far less likely to be hacked than the server running a DB verifying passwords.
Password are simply not where you want to leverage your security. If you can find a document example of a real threat that this approach would have mitigated, then I'll take it seriously.
The downside is not a tiny bit of computation time. It's also increased latency for the customer.
If the server is compromised, then there is no protection of your cleartext password at all. This is because the entity that compromised the server can replace the original JS with anything, including new JS that sends your cleartext password off to their own host as you type each character.
The only activity on your part that can save you against comprimised servers is having a unique password per server (i.e., not reusing any passwords).
An example of where it might work is in an app, where you're getting the client code from a separate channel like an app store.
About client side benefits. I'm not advocating for JS in the browser but there are benefits to doing some work client side.
And if you don't trust HTTPS to protect sensitive information, why would you send the auth cookies over it that have virtually as much power the password that was given in exchange for them in the first place?
There is no reason you can't also salt on the client. Salts do not need to be secret. The substantial constraint you outlined in your comment isn't a problem.
If you see a password you can leak it by screwing up in numbers of ways. If you never see a password you just can't leak it.
E.g. Twitter recently discovered that they were storing passwords in plaintext in logs, GitHub had similar issue.
Take a look here: https://arstechnica.com/information-technology/2018/05/twitt...
Of course, a hash that you will recive from client should be treated as a normal password including all good practices.
Hashing the password before sending it doesn't really help you much - the naïve approach is vulnerable to "pass-the-hash" (where you basically send the hash instead of the password as the authentication token). The secure approach involves either some kind of challenge-response or a nonce salt, but these aren't as easy to implement correctly.
So, not cleartext over the wire then.
Furthermore, pre-hashing doesn't necessarily make transmitting confidential information safer, as one would argue that your client side javascript can be reverse-engineered and give the attacker more information about how you hash your data.
pre-hashing doesn't prevent an attacker from stealing your account if it can read the communication, but it prevents it from having your password and using it everywhere else where you might re-use the password or a permutation of it
Ideally, if TLS was being MITMed somehow such as a dodgy root cert. It would shield the users plaintext password so it could not be used to login into other services. The problem is as soon there is TLS issue an attacker can modify the Js to just send the password in the clear. It really would require code that can't be modified by attacker. This means that there would have to be some sort of browser support. Otherwise it does nothing against the attack it would protect against.
The main benefit is offloading some computation workload on the clients machine. This could allow you to increase the work load required to brute force the password hashes assuming your database leaks. (aka increase iterations or memory requirements)
You last argument is security through obscurity if exposing how you hash makes it easier to brute force the passwords your password hashing sucks.
TLDR don't do that, send passwords over SSL and use a good password hashing algorithm on the server like BCrypt.
None of bcrypt, scrypt, or Argon2 use them and are not materially worse for it.
[1]: https://sudo.pagerduty.com/for_engineers/
[2]: https://www.owasp.org/index.php/Password_Storage_Cheat_Sheet
Your password _is_ whatever you send over the wire. Doing a hash in JavaScript before sending it won't obscure the user's password from anyone who can see their traffic; it will obscure the user's password from the user.
Why would you want to see actual user password if you can just not see it?
If you see a password you can leak it by screwing up in numbers of ways. If you never see a password you just can't leak it.
E.g. Twitter recently discovered that they were storing passwords in plaintext in logs, GitHub had similar issue.
Take a look here: https://arstechnica.com/information-technology/2018/05/twitt....
Of course, a hash that you will receive from client should be treated as a normal password including all good practices.
So, there are properties that differentiate "password" and "5f4dcc3b5aa765d61d8327deb882cf99", even if for the server it's all the same.
Blizzard entertainment does half client half server hashing which is rather clever, one of the few examples where client hashing makes sense.
The best protocol I know of is to derive a signing keypair from your (salted, stretched) password, and store the public key on the server instead of a password hash. Then during login, the server sends a challenge to the client, and the client signs it. The server never sees any secret material at all. Keybase uses a version of this protocol.
Unfortunately all the magical client side crypto in the world doesn't save you if the attacker can compromise your server and then send clients bad JS :p
Why would you want to see actual user password if you can just not see it?
If you see a password you can leak it by screwing up in numbers of ways. If you never see a password you just can't leak it.
E.g. Twitter recently discovered that they were storing passwords in plaintext in logs, GitHub had similar issue.
Take a look here: https://arstechnica.com/information-technology/2018/05/twitt....
Of course, a hash that you will receive from client should be treated as a normal password including all good practices.