Is it worth hashing passwords on the client side

Viewed 109497

When I want to put a login system in place, I always compare the MD5 of the given password with its value in the users table on the server side.

However, a friend of mine told me that a "clear" password could be sniffed by a network software.

So my question is: is it a good idea to hash the password on the client side? Is it better than hashing it on the server side?

13 Answers

Note that keeping passwords secure against third parties parties is not all there is to it.

As soon as privacy is involved (and when ever is it not, these days?) you don't want to know the password. You can't abuse or leak what you don't have, so both you and your clients can sleep better if you never ever see their clear-text passwords.

Therefore, hashing/encrypting client-side makes sense.

Recently both GitHub and Twitter announced that passwords where stored in internal logs. I've had this happen inadvertently in bug reports and other logs that found their way into splunk etc. For twitter if Trump's password was in there log it could be a big deal for an admin to "see", for other sites probably not as big of a deal as the administrators wouldn't have much use for it. Regardless as admins we don't like seeing the passwords do we.

So the question is really if the hashing should happen client side for security, but how can we protect the password before it ultimately gets hashed and compared by the server side so it doesn't get logged somehow.

Encryption is not a bad idea because the developers at least have to jump through some hoops, and if you find that passwords got into logs you can just change the encryption key, destroy the original, and that data becomes useless. Better yet rotate the keys nightly and it could reduce the windows greatly.

You could also hash a hash in your user record. The leaked passwords would be hashed plain text passwords. The server would store a hashed version of the hash. Sure the hash becomes the password, but unless you've got a photographic memory you are not going to remember a 60 char bcyrpt. Salt with the username. If you could gather something about the user during the login process (while not exposing that the user record exists ) you can salt with that as well creating a more robust hash that couldn't be shared between sites. No man in the middle would be able to just cut and paste the captured hash between sites.

Combine with a cookie that doesn't get submitted back to the server and you might be onto something. On first request submit a cookie to the client with a key, then make sure that cookie doesn't make its way back to the login service so little chance of it being logged. Store the key in a session store, and then delete it right after login occurs or when session expired... this requires state for you JWT guys, but maybe just use a nosql service for it.

So down the road an admin comes across one of these hashed and encrypted passwords in splunk or a bug reporting tool. It should be useless to them as they can't find the encryption key anymore, and even if they did they then have to brute force a hash. In addition the end user didn't send anything plaintext along the line so any man in the middle at least has a harder time of it and you can't just hop to another site and login.

This idea of client side hashing is to protect the user, not your site. As mentioned many times already, plain text or hashed passwords both have access to your site equally. You don't get a security benefit.

But your users actual, plain text, password should only be known by them. Knowing what they chose as a password is information that can be used against them on other sites and systems. You are being a customer-focused site by protecting them from having their password choice discovered by your server devs or by third parties.

Consider this:-

Client sends request to server "I have a password to validate".

Server sends client a one-time only random string. R$

Client embeds the user's password in this string (based on any (variable) rules you wish to apply).

Client sends the string to the server and if password OK, server logs user in.

Should server receive another login request using R$, user is logged out and the account is frozen pending investigation.

Obviously all other (normal) security precautions would be taken.

The method that I have come up with is to use SHA-256 with multiple rounds and a random salt...

salt = generate random salt
hash = sha256(password)
for (i = 0 to rounds)
    hash = sha256(hash + salt)

This encrypts the password in such a way that only the user knows what the password is. Both salt and hash are stored in the database. I have also implemented this on the client side using a server generated random string.

server_data = {
    algorithm,
    rounds,
    salt,
    string
}

hash = hash(server_data.algorithm, password)
for (i = 0 to server_data.rounds)
    hash = hash(server_data.algorithm, hash + salt)
hash = hash(server_data.algorithm, hash + server_data.string)

In crypto class, we were taught that the attacker can know the algorithm, salt, rounds, etc.... But in this case, the actual hashed password is never sent. The server knows what the random string that it sent was, so the full hash is generated on the client, and the final piece is to hash that result with the random string. That's the result that's sent to the server. The result is effectively a one-time password that is useless on replay attacks. The user's password is never sent in the clear, and if the attacker gained the password hash, it would be useless to them because the server will not accept it in that form. The hacker MAY be able to compute a rainbow table for the salt and the number of rounds to try and brute force the password, but any sufficintly complex password using ASCII symbols 32-126 of at least 8 characters long will usually take a long time to crack...and that's for EACH password. The sites that I write for can have passwords up to 128 characters in length.

As for session hijacking, that risk can be reasonably mitigated by regenerating the session ID every 30 seconds or so. I also include a token that's embedded in the HTML of the page itself. On the server side, some of the client information such as the client's IP address and user agent string are hashed as well and stored in the user's session data.

So on each request, the server gets the cookie session ID and the token. The token is compared to what is in the session data. The other information such as the IP address and user agent are hashed and compared to what's in the session as well. If it all matches, then there's a very high probability that this is the real user and not an intruder.

The only way that I can see this happening is if the attacker has the embedded token, the same IP address, and the same user agent. It's all about probabilities. Besides, any time you are dealing with crypto, it can be hard to get right. So as others have said, don't do it yourself.

It's 2022 now. As hacker attacks are getting more sophisticated, luckily also have browser standards. The defacto transmission protocol is encrypted (https) and cross site scripting or replay attacks systematically are more difficult with content security policies and CORS.

I believe your time will be well spend familiarising yourself with these technologies.

I'm not a security expert and leave that in the hands of more capable engineers.

Related