Lets take it step by step.
- A (secure) hash takes the password and applies either a key or salt to the plain text resulting in the "Hash". Let us assume that this is done in the browser
- The browser now submits this hash along my by user name (or similar) to the server over a HTTP connection
- The server receives the hash and user name, and looks this up in its database
- The server can't do anything with the hash, it doesn't know the password, it has to use what it got
- It therefore can only store what was given (sure it could hash it again, but that doesn't add any security to the password), so it does a check against what it stored
- User is "signed in"
- Sadly for this user, they were using an open WiFi connection at a local restaurant... I'm sitting on the same WiFi network running some tools that allow be to dump WiFi traffic in promiscuous mode.. Hum.. interesting a signing request to insecurebank.com, I wonder...
- I take the plain text http request I saw on the wire and play it again. What happens?
- Server sees request, server looks at user name and hash, server validates hash. Bingo. I can be you.
- The hash is the password
Errr, well, if you implement it that way, however the problem of replayable hashes was solved a long time ago.
A more secure approach is that the server sends the client a challenge containing a random key. This is combined with the password and sent back by the client. The server also computes the hash and compares what the client sent, if they match then you are in.
Only if you know the password (which never goes over the wire) and the challenge are you able to compute the hash and the hasing functions are, obviously, one way so that the password (or password as mangled by the random number) cannot be recovered from them.
Ok, fair call if you have a shared secret you are trying to prove one or both sides share (this is a common enough technique, but doesn't belong on a web page in Javascript), but you are still left with the question of what is stored on the server in the case of a password/credential?
Both ends need to be able to create a hash of the data in question. Lets assume this is a password. Now to do this properly, we should be using something like a HMAC structure (there are known weaknesses in doing just HASH(KEY + SECRET) or HASH(SECRET + KEY), but that is not relevant here).
What does the server store in its DB to be able to perform this hash? We need to be able to do the same computation as the client. There are 3 options that I can see.
- Store the password plain text so the server can compute HMAC(Key, password)
- Store the password encrypted so the server can compute HMAC(Key, DECRYPT(decryptKey, password))
- Store HASH(password), the client then computes HMAC(Key, HASH(password)) which can again be verified on the server
The first two are bad simply because we now have unlimited access to the password on the server and I believe that we all agree that storing passwords is regarded as poor form. So, we are left with the third option, storing a hash of the original password.
But for all three options, we are now stuck - how do we securely send this password/hash to the server without exposing it when I create my account or change my password? If I can see you send that original password hash I can now correctly respond to any challenge key you issue from the server. This HASH(password) is now again effectively the password.
At some point you will need to securely send the password (or hash) to the server so all the additional code you have used to hash the password on the client still relies on a secure connection.
So just do it the way it was designed in the beginning - https because this provides additional levels of security (authenticating the server to the client). How does your Javascript hashing code determine that it is talking to the correct server? I can setup a proxy between you and the server and truly Man-in-the-middle this process.
Now the next problem is how are you maintaining the "session" state? normally this is handled in a cookie (or a query string argument) but access to this cookie is now access to your identity. Again without HTTPS encryption, there is no way to protect this data and again, If I can get this cookie, I can access your account on the server.
This is the problem that the HTTPS-all-the-thingz campaigns are about fixing.
I see these issues all the time. There is a lack of understanding by a lot of web developers, even of quite popular systems used by many sites.
For instance, even the forum code used here on EEVBlog will let you happily sign in using HTTP, then proceed to issue you a session token. Again all over HTTP, using some javascript client side "hashing". It simply isn't secure. Make sure you use HTTPS. If you do sign in using HTTPS, the forum software doesn't seem to set the HTTPS only flag on the session cookie, so if you then hit the server with HTTP it is still sent and your account exposed.
The thing is, you as the user have to make a call about what information is important enough to protect. Personally, there is very little information about me here and I use large random passwords which are all different for all my accounts so I'm not too worried about leaking my password. I make the judgement that I get more utility using the site than what I risk.
Ash.