Channels ▼
RSS

Web Development

Secure Login in AJAX Applications


Implementing login correctly is a surprisingly complicated bit of coding, particularly in an AJAX application. For example, an HTTP-delivered web page can't use HTTPS to talk to the server to protect the password. You can handle this situation in a klunky-but-easy or elegant-but-hard way, but you have to handle it. This article looks at the various obstacles, describe the solutions in code, and points to some important best practices. Next month, I will look at a full implementation of a generic login system that works on both HTTP-delivered and HTTPS-delivered pages.

Storing Passwords

The first issue is universal, and must be done regardless of how you implement login. The fundamental principle is: Never store an unencrypted password in a database.

As a general rule, you should write your code under the assumption that your server is vulnerable. Passwords are particularly sensitive, because many users use the same password for every website they access, (including their bank). Even if the data associated with your particular app isn't particularly sensitive, don't assume that it doesn't much matter if the passwords are secure on your site. You really don't want to give a hacker the password to your users' bank account. Consequently, not even you should know your users' passwords.

This problem is easy to solve using cryptographic hashing. The basic notion is that a cryptographic hash (sometimes called a message digest) is a longish number (typically 128 bits or more) that uniquely identifies a block of text (anything from a book-length chunk of text to a password). Change one byte of the input, and something like half of the bits in the hash should change. The number of hashes is so large that the odds of two different texts hashing to the same value are astronomically low. It really doesn't matter if this collision does happen, because collisions don't affect security in most protocols. Every time you hash the same input, though, you get the same result. Finally, you can't revert from the hash back to the original value. Given a hash of a password, there's no way to "unhash" it to get the original password.

The basic idea is to store a hashed version of the password in the database (Figure 1). When you validate a password, the server hashes the presented password, then compares that hash to the stored hash. If the two hashes match, then the passwords match (Figure 2).

Figure 1: Storing the password.

Figure 2: Validating the password.

One way to attack a site is to grab the set of hashed passwords, then create hashes of likely passwords (when I was at college, something like 80% of the passwords in use were characters from The Lord of The Rings). Another attack involves taking an entire dictionary of the English language and hashing every word in it. You then compare the hashed dictionary words to the ones that you've stolen. If the hashes match, then you've found the passwords. Similarly, two identical passwords will hash to the same value, so if you break any one of them, you have all of them.

Both of these problems are solved by something called salt. The basic idea is to add a string (the salt) to the password, typically by XORing the characters in the salt to those in the password in somewhat complicated ways. Because usernames are unique, they work pretty well as salt, but you can also just use a random number (cryptographically superior, but annoying in practice becasue it has to be stored somewhere). You should always salt the password before hashing it.

Several cryptographic hash algorithms exist, but some of them (MD5 and SHA1) have known vulnerabilities. Your best bets are the algorithms called SHA-2 (which come in various flavors with varying levels of security — SHA-256 is adequate for our purposes) and BCrypt (which is based on the Blowfish encryption algorithm). I've been using a Java BCrypt implementation that I got from http://www.mindrot.org/projects/jBCrypt/.

Create a hash like this:

 String hashed = BCrypt.hashpw( password, BCrypt.genSalt() ); 

The genSalt() method returns a random number, but you could use the username if you like. Verify the password like this:

 if( BCrypt.checkpw(candidatePassword, hashed) ) // candidate matches 

The string returned from hashpw() has the salt encoded into it, so it doesn't have to be stored separately.

Just for fun, and to demonstrate why you don't want to use them, here's part of what you'd do to accomplish the same thing with the Java Encryption APIs:

byte[] salt = new byte[]{ (byte)0x3a, (byte)0x44, ... , (byte)0x31 };
int iterations = 1000; // should be >= 1000
char[] password = getPasswordFromUser();
PBEKeySpec spec = new PBEKeySpec( password, salt, iterations );
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBEWithMD5AndDES");
SecretKey key = factory.generateSecret( spec );
Cipher    c   = Cipher.getInstance("PBEWithMD5AndDES");
byte[] hashedValue = c.doFinal( plaintext );

Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.
 
Dr. Dobb's TV