grima repository

The Concept

Welcome to the age of information. In this wonderful world all your data, mails, tasks, calendar, etc. are stored in remote server. You also probably participate at some point to a site, a blog or a forum. All theses data makes your personal virtual identity, accessible from anywhere, anytime. And the only things that makes a difference between those structured data and a random chaos is that you control all of theses with you password.

So here comes the problem : majority of web-services use a clear-text password transmission to log you on. If you could at home have a little guarantee that no one is spying upon your connection, this is far from true from every place you want to connect. Practical example of this is Wifi or shared connection at work or school. So you've the choice to not using you preferred web-services or to send over unencrypted channel your unencrypted password.

But there's solutions ! An obviously one, when possible, is to use SSL. Another one is not to send your clear-text password, but a hash (a numeric signature irreversible) of it, or even better, respond to a server challenge. This require to adapt a very little the source code of your web-app, but you'll gain a lot of security.

In the rest of this article I'll show you a demonstration of such an application wich is absolutely transparent to the user, backward compatible for browser without JavaScript and standard compliant.

Underlying Technologies

Firstly I decided to use PHP and JavaScript as primary language for the client and the server. PHP is not only pretty popular but is also used in many of the WebApps I actually use and JavaScript (which should be called EMCAScript in fact) is a defacto standard for Web client scripting.

To makes the hash of the password we'll need an implementation of such a protocol. I use MD5 which is wide spread and has many implementation on many language. If you're very paranoid you also perfectly could use SHA-256. After looking a little around, I choose this implementation from Paul Johnston which work pretty well.

To manage the challenge response with the server we'll use the well know buzzwordy AJAX. Since it's completely useless to reinvent the well, I choose this AJAX abstraction layer which is simple, clean and tiny.

The practical implementation

So here is our classic login form we won't change it at all, so if JavaScript is disabled it will be still fully functional (but unsecured) :

<form id="loginform" method="post" enctype="application/x-www-form-urlencoded" action="login.php">
<fieldset>
	<label for="user">User :</label>
	<input type="text" name="user" id="user"/>
	<label for="pass">Password :</label>
	<input type="password" name="user" id="pass"/>
	<input type="submit" value="login"/>
</fieldset>
</form>'
 

But we'll have to add a little XHTML to this logon page to include our two library and our code. Then we'll have to attach to the submit event of the form.

<html><head>
<script type="text/javascript" src="jxs.js"></script>
<script type="text/javascript" src="md5.js"></script>
<script type="text/javascript" src="securelogin.js"></script>
<script type="text/javascript">
 
 
addLoadEvent(function() {
	document.getElementById('loginform').onsubmit = function()
	{
		return sl(document.getElementById('user').value, document.getElementById('pass').value);
	}
}
 
</script>
 

On server side, we'll create a little securelogin.php which will be charged to handle the challenge (using microtime):

 
session_start();
 
if (isset($_POST['user']) &&
	isset($_POST['pass']) &&
	isset($_SESSION['sl_seed']))
{
	$pass = md5("test password");
	// should be someting like
	// $pass = user_getpass($_POST['user']);
	$pass = md5($_SESSION['sl_seed'].$pass);
	
	if ($_POST['pass'] == $pass)
	{
		// user correctly logged act consequently
		// like setting a session var or loading his profile
		echo "OK";
	} else {
		echo "NO";
	}
} else
	echo $_SESSION['sl_seed'] = microtime();
 

Obviously, this is the file you'll hack a lot. You should implement in it user's password retrieval (my code assume that users password are stored as MD5 hash of real password).

Finally here comes the securelogin.js file. As you see it firstly call the securelogin.php with a simple GET request to retreive the challenge. Then it hash the password and the challenge and send it to the server. The answer of the server don't really import since it reload the page (and if you've well constructed your form it will redirect to the logged interface).

 
/*
 * Global var
 */
 var user;
 var password;
 
/*
 * Main secure login function
 */
function sl(usr, passw)
{
	user = usr;
	password = passw;
	jx.load('securelogin.php', sl_post, 'text', 'get');
	return false;
} 
 
function sl_post(seed)
{
	// hash of the pass
	password = hex_md5(password);
	// hash with the challenge
	password = hex_md5(seed + password);
	// send
	var url = 'securelogin.php?user='+user+'&pass='+password;
	jx.load(url, function (data) { location.reload(); }, 'text', 'post');
	// reload the page (we should be logged
}
 
/*
 * window load attachement function from
 * http://simonwillison.net/2004/May/26/addLoadEvent/
 */
function addLoadEvent(func) {
  var oldonload = window.onload;
  if (typeof window.onload != 'function') {
    window.onload = func;
  } else {
    window.onload = function() {
      if (oldonload) {
        oldonload();
      }
      func();
    }
  }
}
 

Here is the completes files you'll find useful to implement this secure weblogin. Don't hesitate to look at securelogin.js which contain the core of the application.