For a couple of years I had an on and off discussion with some of my peers on hashing passwords on the client as well as the server.
Let me clarify the scene. We are talking about a setup where one user John is connecting to a server. The server uses https communication and has a valid DNS setup.
We are guaranteed to have a secure channel between John and the server. This means as soon as the browser of John submits the tcp packets to the operating system they are secure to tinkering and eavesdropping from outsiders until they reach the server application code where the decryption takes place.
But outside of these points the unencrypted content of the message is visible. This is the key part which leads me to my advice: To hash passwords clientside before transmitting.
Suppose John fills out a login <form>. On submit usually a HTTP POST is done to some enpoint on a server. By default the web specification doesn’t change the input for the password or username in any way. Opening the developer tools of your browser shows you the contents send to the server before encryption takes place.
For example I used a login form to a website written in PHP. It internally sends the following characters
csrf_token=_620fe34dbcd7d4300ada239171084a2d5c50cd7a&j_username=JohnDoe&j_password=Passw1rd%21
I don’t criticise that you can read out the value here.
But consider a slightly different setup: John uses an http connection to an intermediate proxy server. This is often done inside company setups. John connects to an intranet proxy that only allows packets from within the company, then the connection gets encrypted using mutal TLS authentication (duplex TLS, both sides verify certificates) and finally the packet reaches a reverse proxy and from there the application server.
Now the setup reveals some bridges of unencrypted traffic. Under normal circumstances these packets can only be read by operators of the infrastructure and maintainers of the application servers and proxies. But now consider a fault in any of those components. Let it be the proxy server.
What would the fault be? Maybe it’s actually allowing outsiders to access packetlogs, including the raw unencrypted content. Or let the fault be at the application server. All login requests are logged to a logfile. This logfile is downloadable or get’s leaked to outsiders. Or let the fault be in one of the routers between the reverse proxy and the application server, misrouting the packets to an outside server.
To be clear: we are suspecting a fault to be happening here. But as you know – software has faults. It is impossible to write software without faults. And you also know infrastructure’s weakest link are users with just enough technical knowledge to open a firewall port to the public.
In all of the above cases we get a datafile with a structure like this
JohnDoe=Passw1rd%21
RobinHood=Hunter1
...
This is a major problem! And can be solved: what if the file looked like this instead
JohnDoe=7f133a6057292ad4d7776b0c79a4a6830cdc75d879b096c2c7dd7bbe5b0d5061
RobinHood=ef62ccd60470f47e82fcf9500e224273ba301e72053bda81c4e8c76490a48e74
...
I changed the cleartext passwords to SHA256 hashes of them. This action is generally a one-way operation. Meaning getting the Passw1rd%21 string from 7f133a6057292ad4d7776b0c79a4a6830cdc75d879b096c2c7dd7bbe5b0d5061 is hard (in human terms impossible). To be clear, you could still login with this new hash of the password, but what changed is we don’t see the password in its original plaintext form.
This has the major advantage that John, who probably has used this password on other websites aswell, is now safe from getting all their accounts stolen.
This is the whole point I’m trying to make. If the unencryped password gets leaked at some point during the transfer, an eavesdropper could only see a hash, which they couldn’t use to login into other websites.
To make this idea viable in real world you would need to add a salt and pepper to the hash, but this is not the point I am trying to make here.