Securing Passwords with Javascript and MD5

Recently, I posted about Session Hi-Jacking, which is a tactic for taking over someone's session on a website without their knowledge. Session Hijacking is one of the unfortunate consequences of not using HTTPS to protect your internet connection full-time. (If you want to know what that is, I wrote about it here too!) The unfortunate truth is that for a large part of our web browsing experience, our data is being transmitted in a format that anyone watching could read. Most times, this is ok... but sometimes it isn't. For most websites you visit, you will find that your login happens over HTTPS, but the rest of your session does not. That is because a session is temporary, and has no impact once it expires - so most people don't worry about it because for the longest time you had to have some knowledge of Network Infrastructure and Traffic Sniffing in order to hi-jack a session. The Firefox plugin Firesheep made it as easy as installing a Firefox extension. Big problem, but it's still only a session.

The bigger problem is that these days there are still websites that do not encrypt anything at all. Indeed, many smaller websites will transmit passwords in plain text over the internet, for anyone to read. Much of the reason for this is that SSL certificates to use HTTPS are expensive. It can be cost-prohibitive for a small business to pay for a secure certificate for their website, or for a contracting organization to pay for certificates for every single client without passing that cost along to the client.

Don't believe me? Until recently, this very site suffered from that same problem. As another example, The University of Georgia's Red and Black website  does not encrypt logins either. So, if any of my readers have web access to the Red and Black, you might consider making sure your R&B password isn't the same as your UGA MyID, Facebook password, or email password, because right now anyone could read it in transit. Especially if you're using the site over UGA's PAWS wireless - which does not have any encryption of its own. Just a helpful hint.

The Situation

While many of my contemporaries could argue that this is all just a matter of threat modeling - that is applying different levels of security to different levels of power - the fact of the matter is that most of the end-users we interact with don't understand this. Joe Schmoe is more likely to use the same password for everything from his blog to his online banking system. Let's be honest, your average server administrator is just as likely to do the same.

Unfortunately, companies like to charge through the nose for their treasured SSL certificates. And more often than not the bottom line takes precedence over good system design. I find that somewhat irresponsible, but when you're working in a client driven industry it's frequently unavoidable. So, what do we do? We can't or won't shell out the change to pay for the best security out there - but there doesn't seem to be an alternative.

The truth is we now have web browsers that are capable of doing significant computations through Javascript. So much so that Javascript is a heavy part of many modern websites. So, I thought, what if we used Javascript to protect people's passwords over a regular HTTP connection?

Welcome to CRAM-MD5

Anyone who has an extensive knowledge of cryptology probably knows that CRAM-MD5 stands for "Challenge Response Authentication Mechanism, MD5". For anyone who is interested, this is the security method used by default in the subversion VCS, and I'm arguing that we should make it the default in all of our web applications as well.

While the idea isn't entirely unique (there have been some similar implementations in the past), none that I've seen before really get to the heart of CRAM-MD5. Anyway, here's how it works:

  1. The end-user enters their username and password into the form to log in as normal.
  2. When they click login, the browser does an AJAX request to the server behind-the-scenes to get an some encrypted challenge sequence (really, just an MD5 Hash).
  3. When the browser gets the challenge, it then encrypts the plaintext password the user entered using MD5. Then, the MD5 password is concatenated onto the end of the challenge text - and the entire thing is run through MD5 again. (DOUBLE MD5 FTW!)
  4. The resulting MD5 hash is then set as the password value on the login form, and the form is submitted. The server checks to see that the values match what it expects and, if so, the user is logged in.
  5. After login, the challenge string is cleared, and the server will not accept the same challenge string for that user again.

For maximum effectiveness, a few things must hold true:

  • The challenge strings should never repeat. The server should generate a new challenge string each time the user is logging in.
  • The challenge strings should never be predictable. The best way to do this is to plug a couple of random numbers into the generator for the challenge string.
  • Challenge strings should be invalidated on successful or failed login. This should only leave a window of about a second that the correct response string will allow for successful login, preventing an attacker from capturing a response string - then sending that same response string to authenticate again on his computer.

Security Analysis

The frist question that I imagine I will be asked is, is this as good as SSL? The answer is "of course not". Nowhere near it, in fact.

First off, MD5 has been proven to be broken. There are stronger algorithms that you could use with this tactic, i.e. SHA512. So, in theory, it could be cracked. It will never be as good as the asymmetric encryption provided by SSL/TLS. There's just no way around it.

On the pro side, this solution is certainly better than transmitting plaintext passwords because it raises the bar for the effort that it takes to decrypt the actual password. Unless someone is determined to get into your site, odds are they'll go after another site with easier security. Another item for the pro column is that this is a solution that is incredibly easy to implement in web applications. Web Application Developers like those behind products such as WordPress could implement this feature - and most users would never notice the difference. Of course, as always the encryption partially depends on good password selection. As long as people continue to use things like "admin" and "password" as their passwords, we will still have issues.

On the con side of the equation we have the added complexity of requiring Javascript to be active to allow login. Of course, most Web Applications are so riddled with Javascript anyway, that it doesn't really matter anymore.

This authentication solution is not a replacement for SSL. However, I will argue that this technique is a good economy substitute to SSL encryption for logins on low sensitivity sites, when combined with a good password. And hey, crackable or not, it's certainly better than transmitting plaintext passwords over the wire. That's for sure.

In Closing

If people are interested in me creating a library that does this I can, but to me it seems like a pretty simple implementation so I haven't bothered. I would also be interested to hear about any other ideas that you have thought of/implemented for password protection in environments where SSL was not used for one reason or another.

As always, leave me some comment love, and I'll be posting at you guys again soon.