After reading this article (which was linked off Digg), I was surprised to find that many commercial and popular sites were still sending login and password information in plaintext over unencrypted channels as part of their authentication system. Most login forms use the POST HTTP method to submit information to the server, and while this doesn’t reveal the contents in the URL as a GET request would, the information is easily available if one looks at the HTTP headers – and if the passwords are submitted in plaintext (or a simple encoding such as base-64 is used), then it’s trivial for someone sniffing packets to obtain your password and hence your account credentials.
While this lack of security surprised me, what further surprised me were the number of people throwing their arms up, and saying, “Oh well, this is unpreventable when you’re not using SSL (https) for your client-server connection“. Contrary to popular belief, it is possible to implement a login or authentication system that doesn’t transmit passwords in plaintext and thus is more secure over unencrypted channels – it’s called a CHAP login system, and it vastly improves security.
The basic problem with communication over the Internet is that it is inherently insecure. Eavesdropping is quite easy, especially if one has knowledge of how the packets or traffic are being sent from a host to a server and vice-versa – this has to do with how most of the networks that comprise the Internet are setup. This presents a problem during the process of authentication, that is, determining that a user really is who they purport to be.
Authentication usually takes the form of a user providing credentials, that is, a set of information that proves their identity. For most applications, this consists of a username-password combination, which provides both the identity (username) and proof (password). The password is proof of the user’s identity since it is supposed to be kept a secret. But, if the password is transmitted over insecure channels, how can it be kept a secret?
CHAP and Digest Access Authentication
This is where CHAP (Challenge-Response Authentication Protocol) comes into play. Despite my main focus being on web applications (built using server-side scripting languages), methods like this such as Digest Access Authentication have been around for some time. Digest Access Authentication is a function provided by the web server itself (though the client browser must support the method as well), and is a way to password-protect resources (i.e., certain web pages or directories) so that only certain users can access it. The previous Basic Authentication Scheme, merely transmitted both the login and username after encoding in base-64, providing no protection whatsoever from sniffing.
So, how does it work? At the heart of CHAP are functions known as hash functions . Ideal hash functions are one-way, that is, if one knows the output of the function, it is impossible to determine the input. One output could potentially correspond to an infinite number of inputs. How can this be so? Think of functions involving modular arithmetic – if I tell you that the remainder of a division was 3, can you come up with a definitive answer as to what the intial two inputs were? It could have been
15 mod 4, but it could have just as easily have been
16 mod 13 . Actual hash functions are more complicated than this.
Another important property is that different inputs should produce different outputs – the previous example did not illustrate this. To some extent, this property can only be idealized, since the output from hash functions are usually strings with limited lengths, and thus there are only a finite number of outputs while there are usually an infinite number of inputs. Thus, a good hash function should try to avoid these collisions, that is, when two inputs produce the same output, and should definitely make it hard to intentionally produce collisions.
Bringing it together
Perhaps you can see how hash functions would be ideal in this situation. For example, a password, once put through the hash, would produce an output that could still be used for authentication, but it would be impossible to go from this back to the original password. But, CHAP takes it one step further.
When a user wants to authenticate, the server first generates and issues a random challenge string to the client. The client then combines this with the password using HMAC, (a cryptographically proper way to combine information with a key/password), then feeds this result into a hash function. The output from the hash function is then submitted to the server. When the server receives this string, it also does the same computation (since it keeps hold of the challenge string and also has the user’s password), and compares this with what it received from the client. If the two match, then authentication was successful and the user can be given access!
How is this better than transmitting the password in plaintext? Let’s say someone, named Eve, is sniffing the traffic from client to server. What information would she get? Well, first, she’d get the challenge string issued from the server to the client. Then, she’d get the output from the hash function sent from client to server. Why can she not derive the password from this? Because hash functions are one-way, there is no way to go back original input data – which contained the password combined with the challenge string.
Hold on, partner
Another important point is that the challenge strings issued by the server must be random and must not be reused. If they were reused, our attacker Eve could sniff the output from the client’s hash function and just re-send that to trick the authentication scheme as the same challenge string and password fed into a given hash function will always produce the same output. Therefore, there must be a way to keep track of the challenge strings used or at least make it very unlikely for the same challenge string to be used more than once.
Also, the technique is not totally secure. (But what is?) An attacker could capture the output from the client’s hash function and using brute force attacks (trying almost every combination), attempt to guess the user’s password, by trying to find out what would produce the hash function’s output. This has the added danger that the attacker doesn’t need to communicate with the server for this sort of attack, negating any server-side lockout protection that may be in place. A remedy for this, is of course, strong passwords, but not a lot of people use them.
Additionally, a multi-user system would need to keep track of challenges issued to each user, instead of relying on the client to tell the server what challenge was issued to it. This must be done since mutiple users could be authentication at or around the same time, so the server would need to know what challenges were issued to which user. If the usernames and passwords are stored in a table, this could easily be done by adding another column for the challenge.
The tricky part here is that now the server needs to know the username before it can issue the challenge. This could be done in a two-step process, but might be confusing to some users. Here is where I think Ajax could come into play – using Ajax techniques, you would be able to accomplish this two-step process using a normal form and mask it to appear as a regular one-step login process.
Another issue that needs to be dealt with is that the way this system has been currently described, the passwords are stored in plaintext on the server. This is a big no-no, as if the server is compromised, the attacker now has a plethora of username/password combinations. Since users often re-use passwords across many different sites (despite warnings not to), some of which may have sensitive financial information, this could lead to more complicated matters, such as identity theft. One solution is to store a hash of the password plus a salt value, and then also adjust the client-side method for computation of the challenge-password hash accordingly. This doesn’t improve the security of the system, but it does prevent passwords from being discovered should the server be compromised.
All this talk, but no action