random thoughts, formed in the twisted mind of a coder... RSS 2.0
# Tuesday, September 30, 2008

Most of the times when you write some code for online applications, you need to discriminate between different users. For example, you’re coding a website with a forum. In order to use the forum your users have to login. This is most commonly done by providing a username and a password…

But how do you transfer the password from the client to the server in a secure way?

 

Most of the time you would be able to simply setup an SSL connection (HTTPS) and transfer data across that encrypted line, but there may be cases where SSL is not available. For example: firewalls are in the way, you want to host multiple domains on one HTTP server, it’s too slow, or because (you think) it costs too much to buy a certificate.

 

[please note that you can get free SSL certificates from CAcert  (http://www.cacert.org/)]

 

In such cases you would most probably fall back to sending the username and password in plain-text from the client to the server (ie. using a standard form-post from an HTML document).

 

When doing so, you make it very easy for any hacker (or simply a kid with a tool) to capture the passwords which are sent to your website. With a sniffer you are able to “snif” the data that passes your network (or network card). Sniffers are made to find operational problems on your network, but are also available in the standard toolkit of any self-respecting hacker.

 

Note that sniffers are freely distributed by many large companies. Microsoft provides the Microsoft Network Monitor at no costs. Or you could download Gerald Combs’ WireShark sniffer (formerly known as Ethereal), which is an open-source network sniffer.

 

The best way to prevent anyone from sniffing your password, is to not send it!

 

But how can you authenticate a user without transmitting his password?

With Keyed-Hashing (or Keyed-MD5). It’s used by many email protocols for secured logins, like SMTP, POP3 and IMAP4. Also other login protocols, like Chap, Digest and GSAPI,  share the same foundation.

 

Basically it’s done like this:

1.       The server passes a random string to the client

2.       The client performs a calculation on the password and the random string

3.       The client passes the username (in plain text) and the result of the calculation to the server

4.       The server performs the same calculation as the client did and compares the output to the output of the client.

5.       If it’s a match, the client is authentic.

 

So, because both the server and the client already know the password, they don’t have to send it to each other. They merely do the same calculation, which should create the same result  on both sides because the password (an input parameter of the calculation) is the same on both sides.

 

Now why does the server provide a unique random string? This is because the string is used as an input parameter of the calculation. It makes the result also random and unique. That way the result passed from client to server is always different while the password remains the same.

If you wouldn’t make the hash unique and random the static hash could simply be sniffed and used as a password.

 

I probably know what you’re thinking right now… “But wouldn’t I be able to snif the random string and the calculation result and reverse the calculation and so recalculate the original password?”.

No, you can’t. This is because the calculation uses a MD5 hashing algorithm. If you would try to recalculate the password, you would end up with millions of possible outcomes.

 

[note: some weaknesses have been discovered in the MD5 algorithm. However, they do not weaken your security for the purpose of transferring password]

 

All nice and well, but let’s show some bits.

So what I’ve done is created an example which demonstrates a normal website login. For this I’ve downloaded the JavaScript MD5 library from Paul Johnston (Paj). Check the references for the URL.

This library comes with HMAC functionality included. Because you can download Paj’s script and documentation, I’m not going to explain how this JavaScript implementation works.

 

[Please note that this technique can be used in all sorts of client-server applications, not only websites.]

 

First, let’s see the client-side code:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" >
<head>
    <title>Keyed Hashing</title>
    <script type="text/javascript">
      var unique_string = 'some unique random string’;
    </script>
    <script type="text/javascript" src="md5.js"></script>
    <script type="text/javascript">
    
      function hashPass()
      {
        //replace password with hex representation of hash value
        var elm = document.getElementById("password");
        elm.value = hex_hmac_md5(elm.value, unique_string);
        
        //alow form to post data to the server
        return true;
      }
      
    </script>
</head>
<body>
    <form action="Default.aspx" method="post" onsubmit="hashPass();">
    <div>
    name:<br />
    <input name="name" type="text" />
    </div>
    <div>
    password:<br />
    <input id="password" name="password" type="password" />
    </div>
    <div>
    <input name="submit" type="submit" value="submit" />
    </div>
    </form>
</body>
</html>

So, basically, the HTML puts a form on your screen which lets you enter a username and password. When the submit-button is pressed, the submission is caught by a javascript function. This function performs the HMAC calculation on the password and puts the result back into the password field, so that it overwrites the original password (to make sure the original password is lost). After all that, the form is submitted to the server.

 

Now, before we go on to the server-side code, let’s first elaborate on the client-side javascript code.

 

As you can see, three script elements are included in this document. The first one declares the unique random string. I know, it’s not so unique and random now, but that will change when we put in the server-side code.

The second script loads md5.js, the MD5 hash library.

And finally the third script declares the hashPass function. That function simply reads the password from the form and uses that password, together with the unique string, to calculate the HMAC hash. The hash result is put back into the password element.

 

Let’s put in the server-side code.

As this is a self submitting document, I’m not really able to show you the server-side code without showing the client-side code, so here’s the lot…

<%@ Page Language="C#" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<script runat="server">
  
  //--------------------------------------------------------------------------
  // Fields
  //--------------------------------------------------------------------------
  
  //the static credentials to check for testing purpose.
  //normally, use a DataBase or LDAP to store your credentials
  const string CRED_USER = "rednael";
  const string CRED_PASS = "mypassword";
  
  //the status message for the client
  string status = "";
  
  //--------------------------------------------------------------------------
  // Page-Load event handler
  //--------------------------------------------------------------------------
  protected void Page_Load(object sender, EventArgs e)
  {
    //check if form was posted
    if (Request.Form["name"] != null && Request.Form["password"] != null)
    {
      //check the credentials
      if (Request.Form["name"].Equals(CRED_USER))
      {
        //create the password hash
        System.Security.Cryptography.HMACMD5 hmac = new System.Security.Cryptography.HMACMD5();
        hmac.Key = System.Text.Encoding.ASCII.GetBytes(CRED_PASS);
        hmac.ComputeHash(System.Text.Encoding.ASCII.GetBytes((string)Session["uid"]));
        
        //convert hash to string
        string hash = "";
        foreach (byte num in hmac.Hash)
          hash += num.ToString("X02");
        
        //compare to hash from client
        if (Request.Form["password"].ToUpper().Equals(hash.ToUpper()))
          status = "you are authenticated!";
        else
          //wrong password
          status = "unknown username or password";
      }
      else
      {
        //unknown user
        status = "unknown username or password";
      }
    }
    else
    {
      //no form has been posted yet...
      status = "please provide credentials...";
    }
  }
  
  //--------------------------------------------------------------------------
</script>

<html xmlns="http://www.w3.org/1999/xhtml" >
<head>
    <title>Keyed Hashing</title>
    <%
      //calculate unique random string
      string uid = "";
      uid += (new Random()).Next(Int32.MaxValue).ToString("X04");
      uid += ".";
      uid += System.Threading.Thread.CurrentThread.ManagedThreadId.GetHashCode().ToString("X04");
      uid += ".";
      uid += DateTime.Now.Ticks.ToString("X08");
      uid += "@";
      uid += Environment.MachineName.GetHashCode().ToString("X04");
      uid += ".";
      uid += Environment.UserName.GetHashCode().ToString("X04");
      
      //buffer unique string in session
      Session.Add("uid", uid);
      
      //write unique string into client-side script block
      Response.Write("<sc"+"ript type=\"text/javascript\">");
      Response.Write("  var unique_string = '" + uid + "';");
      Response.Write("</s"+"cript>");
    %>
    <script type="text/javascript" src="md5.js"></script>
    <script type="text/javascript">
    
      function hashPass()
      {
        //replace password with hex representation of hash value
        var elm = document.getElementById("password");
        elm.value = hex_hmac_md5(elm.value, unique_string);
        
        //alow form to post data to the server
        return true;
      }
      
    </script>
</head>
<body>
    <form action="Default.aspx" method="post" onsubmit="hashPass();">
    <div>
    name:<br />
    <input name="name" type="text" />
    </div>
    <div>
    password:<br />
    <input id="password" name="password" type="password" />
    </div>
    <div>
    <input name="submit" type="submit" value="submit" />
    </div>
    <div>
    <br />
    message from the server:<br />
    <%
      //write status message to client
      Response.Write(status);
    %>
    </div>
    </form>
</body>
</html>

There are three different parts in the code now.

The first is an additional script element (runat server) with the code to compare the hashes. The second is the part where the unique random string is calculated. And the third part is where the status message is written to the client.

 

I think the part where the status message is written to the client speaks for itself. No need to dig deeper into that.

The part of creating the unique string is more interesting… This is roughly the way the Cram-MD5 RFC describes how to create a unique string (copied from the 822 RFC, describing the Msg-ID field). I’ve put in a few extra elements (like ThreadID) to make it more unique.

You can of course use any string that is unique to your system. Just keep in mind that your system may run on multiple servers, using multiple threads and that the string should then still stay unique.

 

Now, let’s go on to the first server-side script block.

In there you’ll find the event handler code for the Page-Load event. This method first evaluates the username and calculates the HMAC hash for the accompanying password. It then simply compares the calculated hash with the hash that is posted by the client. If those two match, the client is authentic.

 

As you can see, I’m writing the same status message to the client when the username is incorrect as I do when the password is incorrect. If I wouldn’t do this, any hacker would be able to see if he “guessed” the username correctly and then just focus on the password. By giving back the same answers in both situations, a hacker is left in the dark.

 

 

references:

 

HMAC and Keyed-MD5

http://tools.ietf.org/html/rfc2104

 

Cram-MD5

http://tools.ietf.org/html/rfc2195

 

MD5 Message Digest

http://tools.ietf.org/html/rfc1321

 

Paj’s Home: MD5 in Javascript

http://pajhome.org.uk/crypt/md5/

 

Microsoft Network Monitor

http://www.microsoft.com/downloads/details.aspx?FamilyID=18b1d59d-f4d8-4213-8d17-2f6dde7d7aac&displaylang=en

 

WireShark

http://www.wireshark.org/

 

CAcert

http://www.cacert.org/

Wikipedia Topics
http://en.wikipedia.org/wiki/HMAC
http://en.wikipedia.org/wiki/CRAM-MD5

Tuesday, September 30, 2008 4:49:26 PM (W. Europe Daylight Time, UTC+02:00)  #    Comments [3]
Script and HTML | Security
Sunday, February 07, 2010 7:08:17 PM (W. Europe Standard Time, UTC+01:00)
thanks for providing this information
can u provide information regarding "message authentication in computationally constrained environment"
venkat
Monday, February 08, 2010 4:51:15 PM (W. Europe Standard Time, UTC+01:00)
Hi Venkat...

Please explain what you mean by "computationally constrained environment".

Thursday, September 15, 2011 8:31:40 PM (W. Europe Daylight Time, UTC+02:00)
Thanks for the information. It was very helpful.
One thing I would like to add is that if you are working on a Xmpp client sasl, the Hex string generated has to be in lower case.
so the statement in the code:

hash += num.ToString("X02");

need to be

hash += num.ToString("x02");

I was stuck there for a while. Hope it is helpful to others.

thanks

Xiali
Xiali Zheng
All comments require the approval of the site owner before being displayed.
Name
E-mail
Home page

Comment (HTML not allowed)  

Enter the code shown (prevents robots):

Live Comment Preview
About the author/Disclaimer

Disclaimer
The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.

© Copyright 2014
Martijn Thie
Sign In
Statistics
Total Posts: 18
This Year: 0
This Month: 0
This Week: 0
Comments: 160
All Content © 2014, Martijn Thie
DasBlog theme adapted from 'Business' (originally by delarou)