Advanced Authentication

Most of the ideas in this section are hypothetical, although some have been implemented successfully. I would be interested to hear from people who try to implement anything on this page.

If JavaScipt is not Available

If JavaScipt is not available, the site has a choice - it can either allow a login with the password unencrypted, or block the login altogether. I recommend allowing unencrypted logins; sites that can't accept this risk should probably be using SSL. However, the site should warn the user that their password will be at risk.

To allow an unencrypted login, the server code to check a password needs to be updated. Calculate hex_hmac_md5(server salt, received password); if this matches the stored serve value, login is successful. To warn users of unencrypted logins, have a script block in the login form, with a noscript tag containing a phrase like "Warning: this login will not be encrypted".

If you want to block unencrypted logins, remove the login button from the HTML, and have JavaScript insert it dynamically. If JavaScript is not available, the user will simply not see the login button.

There is the possibility that JavaScript is available, but the hash scripts don't work. This will be very rare in practice. To help cope with this, the scripts have a function like md5_vm_test that returns true if the script is functioning correctly. If the script is not working, you will want to behave as if JavaScript is not available.

Save Password Feature

Most modern browsers have the ability to save passwords for web sites. JavaScript password hashing can interfere with this. Firefox and Opera save the password value before onsubmit is called, so the feature works correctly with JavaScript hashing. However, Internet Explorer, Safari and Chrome save the values after the onsubmit event. To better support these browsers, save the password hash in a hidden field, and blank the password field. In this case, these browsers will not offer to save the password, which is better than saving a password hash that won't work.


For some web sites, including a dynamic challenge on every login form is not practical, for performance reasons. This is particularly true on sites that have the login form on every page. It is still possible to use challenge-response authentication on such sites, and avoid replay attacks; one approach is:

  • Have the login form contain a static master challenge.
  • At login, JavaScript on the client generates a secondary challenge. One approach is to combine the user name and the current time.
  • The client sends (master challenge, secondary challenge, hash)
  • The server checks the secondary challenge has not previously been used, and records it, to prevent replay attacks.
  • Periodically, the master challenge is changed, and when this is done, the database of used secondary challenges can be cleared.

Strengthening Authentication

One option for strengthen authentication is using the Secure Remote Password (SRP) protocol. This is more complicated than challenge-response, but also more secure. In particular, it prevents brute force attacks, and avoids password equivalents being stored on the server. There is a JavaScript implementation in the ClipperZ project.

Session ID is Unencrypted
This can be strengthened, but doing so greatly increases complexity. As well as using a session cookie, each URL needs an individual authentication token. more...
Replay Attacks
If the application can save state on the server, this can be easily fixed. Instead of using a timestamp as the challenge, generate 128-bit random challenges, and keep track of issued challenges. When a login attempt is made, first check the challenge was issued by the server, and then remove it, so it can't be used again. The alternative login system also solves this. more...
Active Interception
There is no known way to protect against this, other that using SSL.
Registration and Change Password
Several ways to strengthen this have been suggested:
  • Encrypting using JavaScript RSA. Unfortunately, JavaScript RSA is too slow with secure key sizes.
  • Encrypting using JavaScript AES, with the old password as the key. However, this doesn't fix registration, and is dubious in the case that a user is changing their password because they're worried the old password was compromised.
  • The alternative login system solves this. more...
Brute Force Attacks
SRP prevents brute force attacks. For challenge-response, several approaches can be used to make brute force attacks harder, but not to stop them altogether:
  • Force users to use complex passwords, e.g. 8 characters, at least one letter and one number. However, the server only ever sees hashes, not the original password, so it can't enforce this. JavaScript password strength checking is being considered for future versions of the script.
  • Avoid the challenge and response being transmitted together. This forces an attacker to capture the traffic in both directions, to conduct a brute force attack. However, this is of limited benefit, as in most sniffing attacks, traffic in both directions can be captured.
  • Increase the computing power needed for brute force attacks. When calculating the response, we can repeat the MD5 operation many times, and an attacker will have to do the same for every password in the brute force attack. Experiments show that 100 repeated hashes in JavaScript is not a performance problem, so this defence is worth applying.
Stored Password Equivalents
Both SRP and the alternative login system solve this. more...

Alternative System

There is an alternative approach to challenge-response that resolves several risks, although it creates some new risks too. It is also based on a challenge, but the arrangement it different. When the server issues a challenge, it already knows md5(hmac_md5(password, challenge)). The client sends hmac_md5(password, challenge). The server performs an md5 on the value it receives, and if this matches the stored value, authentication is successful. This is secure, because to produce a value that hashes to the server's stored value, the password must be known.

Once a challenge has been used, we need to create a new one. To do this, the login form contains a next_challenge hidden field. md5(hmac_md5(password, next_challenge)) is sent to the server. To calculate the login hash, the client needs to know the challenge, which is different for each user. When the user name is entered, the client makes an Ajax call to fetch the user's challenge.


  1. Store the server secret in global application configuration. Note: unlike the server salt in the other system, server secret does need to be kept secret.
  2. Add a column to the user table to store the current challenge.
  3. Modify the login form:
    • Add a hidden field "next_challenge" containing a randomly generated challenge.
    • Add an empty hidden field "next_password".
    • Add a script tag to link to md5.js
    • Add a JavaScript onchange event to the user name field. This makes an Ajax call to fetch the current challenge.
    • Add a JavaScript onsubmit event:
      password.value = hex_hmac_md5(password.value, challenge.value);
      next_password.value = hex_md5(hex_hmac_md5(password.value, next_challenge.value));
  4. Create the Ajax callback:
    • Fetch the user's challenge.
    • If the user doesn't exist, we don't want to reveal this. Instead, return hex_hmac_md5(server secret, user name).
  5. Modify the handler that receives posted login data:
    • Calculate hex_md5(received_value)
    • If this matches the stored hash, the login is successful.
    • Update the stored password and challenge, with values received from the client.
  6. Update registration and change password:
    • Add a hidden field containing a randomly generated challenge.
    • Add a script tag to link to md5.js.
    • Add a JavaScript onsubmit event:
      password.value = hex_md5(hex_hmac_md5(password.value, challenge.value));
      confirm_password.value =
              hex_md5(hex_hmac_md5(confirm_password.value, challenge.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 will need a complex migration process.

New Risks

Information Leakage
However, related to this, an attacker can see when a user has logged in, because the random number changes on every successful login. The change in random number confirms to the attacker that the account does exist.
Non-Standard Approach
While this approach appears to me to be secure, it has not been subjected to anything like the degree of scrutiny that challenge-response login has.

Protecting the Session ID

The idea is to use JavaScript cryptography to perform a challenge-response exchange for each page. The system could work as follows:

  • At login, JavaScript saves the password on the client. One way to store this is a cookie with a random path attribute that doesn't exist on the server, so the cookie is never transmitted.
  • When a page is requested without a valid token (a URL parameter "auth"), the server produces a stub page containing a challenge, and some JavaScript code. The code uses the saved password and the challenge to generate a valid token, and redirects to the new URL.
  • The stub page will need to be coded carefully to work with form posts. For this to work with Ajax requests, all the JavaScript that makes Ajax requests will need to be recoded to handle the stub page correctly.
  • The stub page increases page load time. It can be avoided in many circumstances, by having every page include a challenge, with JavaScript to generate the token when links are clicked. The stub page will still be needed in some circumstances, e.g. if the user has used the browser's back button, and the challenge is now invalid.
  • For efficiency, it is recommended that static files, such as images and JavaScript files, are not protected by this system. For most sites, it is only a minor risk for static files to be accessible without authentication.
  • It is best to avoid saving the password on the client. Instead, a temporary password can be derived from the plaintext password, and the login challenge, and all tokens generated from this.
  • To get the security benefits of this system, replay attacks must also be prevented. more...

This system is purely hypothetical at present. I advise against its use, as a site that requires this level of security should use SSL. However, I would be interested to hear from anyone who tries to implement this.

© 1998 - 2012 Paul Johnston, distributed under the BSD License   Updated:12 Jun 2009