Protecting Passwords

When to use JavaScript hashing

In general, it is best to use SSL to protect passwords. This provides stronger protection than any JavaScript hashing, and is becoming easier all the time. SSL certificates can now be obtained for free. Also, the performance impact of SSL is becoming less (more...). If SSL is used, I recommend that JavaScript hashing not be used - it won't add any security, and could cost you some.

There are still circumstances when SSL can't be used. It generally requires a dedicated IP address, as support for TLS Server Name Indication is not quite widespread enough, so it is not common on shared hosting platforms. Embedded applications, such as web management of a wireless router, may not have enough CPU power for responsive SSL. Packaged applications, such as a forum system, don't usually force the sys-admin who installs it to use SSL, so they may want to provide basic security when SSL is not used.

In most of the situations where SSL can't be used, the security requirements are not high. However, as people tend to reuse passwords, it is best to protect them. JavaScript hashing provides basic security for these situations.

Implementing Hashed Passwords

I generally recommend using an authentication library, rather than implementing authentication yourself. Most languages and frameworks have such a library. At present, few authentication libraries support JavaScipt hashing, only:

  • repoze.who-jscrypto - a set of plugins for repoze.who to support JavaScript cryptography (Python).

If you're involved in the development of a framework and would like to add this feature, do email me.

The instructions below describe in general terms how to implement an MD5 authentication system. They avoid referring to any specific server-side language, so apply equally to ASP.Net and Ruby on Rails. Some language-specific instructions are also available, from other sites:

Technical Details

Session Cookie
When the user does a successful login, the server issues a session cookie. This is effectively a random number that an attacker can't guess. The cookie is used to authorise subsequent access to protected pages. When the user does a logout, the cookie is deleted. This basic design is used by almost all web authentication systems.
Challenge-Response Login
The difference with an hashed login is the "challenge-response" exchange. The server generates a challenge and sends this to the client, as a hidden field in the login form. The simplest challenge to use is the server's current time. Once the user has entered their password and clicked login, the client performs a hash operation including both the challenge and the password. It sends the result to the server, and the server checks this against its own calculation. During this exchange, the password is never transmitted unencrypted. If the hash is captured, it cannot be used again, as the challenge changes for each login. I recommend using SHA1 as the hash algorithm, simply because it is the smallest JavaScript file.
Storing Passwords
The server stores password hashes, instead of unencrypted passwords. A random number unique to the server is included in the hash. This number is called the "salt", and it is not secret. It is also included in a hidden field in the login form. The purpose of the server-specific salt is to ensure that a captured password cannot be used on a different server.
Both login and storing passwords require hashes that include both the password, and a random number. We could simply hash the two strings concatenated. However, there is an alternative technique called HMAC, which uses two hash operations and some extra logic. This is specifically designed to be secure for such purposes, making it preferable to simple concatenation.

Some further technical considerations are discussed in the advanced section. more...

Adding MD5 to an Authentication System

  1. Store the server salt in global application configuration.
  2. Modify the login form:
    • Add a hidden fields for: the server salt, the current server time, and the password hash
    • Add a script tag to link to md5.js
    • Add a JavaScript onsubmit event:
      pwhash.value = hex_hmac_sha1(hex_hmac_sha1(password.value,
                              server_salt.value), server_time.value);
      password.value = "";
  3. Modify the handler that receives posted login data:
    • Check the time stamp received from the client is within the last 5 minutes.
    • Calculate hex_hmac_sha1(password stored on server, time stamp).
    • If this matches the hash received from the client, the login is successful.
    • Consider adding code to cope with JavaScript being unavailable. more...
  4. Update registration and change password:
    • Add a hidden field containing the server salt.
    • Add a script tag to link to sha1.js.
    • Add a JavaScript onsubmit event:
      password.value = hex_hmac_sha1(password.value, server_salt.value);
      confirm_password.value =
              hex_hmac_sha1(confirm_password.value, server_salt.value);

If you have an existing site. the main challenge is how passwords are stored in the database. If they are currently unencrypted, you can do a one-time operation to replace them with hashes. If the passwords are already hashed, you may have some difficulties. You will need to identify the hash algorithm in use, and create JavaScript code to match it.


The following technical risks affect this system, as well as all the general risks that affect web applications. For most sites these are not particularly serious. In general, sites that require greater protection should use SSL, although strengthening the system is possible. more...

Session ID is Unencrypted
Although the password is always encrypted, the session ID is transmitted unencrypted. If an attacker captures this, they can perform a "session hijack" attack, to access the user's account. The same risk exists for sites that use SSL to protect the login process, but revert to non-SSL after the user has logged-in.
Replay Attacks
If an attacker captures a login, it is possible to re-use the password hash, until the timestamp expires. This is generally a minor issue, as an attacker who can capture the login can almost certainly capture the session ID. For web sites that store sessions in a database, this can be easily fixed. more...
Active Interception
Over the web, JS cryptography can only protect against passive eavesdropping, as the JavaScript itself is downloaded over an insecure link. If an attacker can modify network traffic, they can make malicious changes to the JavaScript code.
Registration and Change Password
Both registration and change password transmit a "password equivalent" over the Internet. If someone captures this, they can use it to login to the site. The unencrypted password is still never transmitted over the Internet.
Brute Force Attacks
In a "brute force" attack, an attacker tries many different passwords for a user, until they eventually succeed. The use of challenge-response login creates a new risk here. If an attacker captures a password hash, they can later conduct an offline brute force attack, where passwords are checked without accessing the server. This bypasses any server restrictions, such as locking an account after three incorrect passwords. Some simple steps can make this harder for an attacker. more...
Stored Password Equivalents
The hash the server stores is a "password equivalent". If someone captures this, they can use it to login to the site. However, the protection is still worthwhile, because its main purpose is to stop an attacker using captured passwords on different sites. If someone has captured a stored password from your database, they have already compromised your site.
© 1998 - 2012 Paul Johnston, distributed under the BSD License   Updated:16 May 2011