Sponsored By Aspose - File Format APIs for .NET

Aspose are the market leader of .NET APIs for file business formats – natively work with DOCX, XLSX, PPT, PDF, MSG, MPP, images formats and many more!

Signing Requests (or anything) with Hashes

It’s common practice to use hashes for signing purposes. For example, you can take a hash (MD5, SHA1, it doesn’t really matter) of a file’s byte contents and use that hash to ensure the file hasn’t been modified. It’s also quite common to hash a user’s password – which is essentially creating a signature with a single variable.

Hashes are ideal because they are relatively fast to compute, available for virtually every known language, secure (especially if you add a random value in order to prevent a dictionary attack), and easy to implement. Using a hash signature with a web service is a great and simple way to provide a certain degree of authentication. It’s the approach Facebook, as well as other platforms, use as the basis of their security model for third party applications.

At a high level, our strategy is to define a simple algorithm for creating a hash based on parameters and a previously agreed upon secret value. Then, every request a client makes include a hash. The server takes the request and computes its own hash. If the server’s hash and the client’s hash match, the request is authentic and execution can proceed. Pretend we have a web service for a video game which accepts a score, it’s simplest implementation might look something like:

savescore.axd?sessionId=323393&score=500

Of course, this isn’t very secure. Anyone with basic knowledge of HTTP can easily resubmit the request with whatever values they want:

savescore.axd?sessionId=323393&score=01123581321345589

However, if we sign the request based on the two input values (sessionId and score) as well as a secret value, it is no longer possible to simply change the score:

savescore.axd?sessionId=323393&score=500&h=albkk309sk

So how exactly do we create this signature? Well, sticking with Facebook as our guide, we’ll take each key=>value parameter, sort them alphbetically, concatenate them together along with our secret value. So, given them above query and a secret value of “itsover9000″, we’d create a hash of:

score=500sessionId=323393itsover9000

(notice how we aren’t separating our pairs with & – that’s just how facebook does it, you can add an extra separator or anything else if you want)

Here’s a function that does just that:

public static string CreateSignature(IDictionary<string, string> parameters, string secret)
{
   SortedDictionary<string, string> sortedParameters = new SortedDictionary<string, string>(parameters);                  
  StringBuilder sb = new StringBuilder();
  foreach (KeyValuePair<string, string> kvp in sortedParameters)
  {
     sb.Append(string.Concat(kvp.Key, "=", kvp.Value));               
  }        
  sb.Append(secret);
  byte[] bytes = new MD5CryptoServiceProvider().ComputeHash(Encoding.UTF8.GetBytes(sb.ToString()));
  StringBuilder data = new StringBuilder();
  for (int i = 0; i < bytes.Length; ++i)
  {
     data.Append(bytes[ i ].ToString("x2"));
  }
  return data.ToString();
}

(The last part turns our hashed byte array into a hexadecimal format)

Depending on exactly what it is you’re trying to do, you can integrate that directly into your framework. For example, if you’re using HttpHandlers, you could easily build a base class to handle all of this:

public abstract class SignedRequestHandler : IHttpHandler
{
   public void ProcessRequest(HttpContext context)
   {
      NameValueCollection parameters;
      if (string.Compare(context.Request.HttpMethod, "GET", true) == 0)
      {
         parameters = new NameValueCollection(context.Request.QueryString);
      }
      else
      {
         parameters = new NameValueCollection(context.Request.Form);
      }
      string clientHash = parameters["h"];         
      parameters.Remove("h");
      if (string.IsNullOrEmpty(clientHash) || clientHash != CreateSignature(parameters, "itsover9000"))
      {
         throw new RequestSignatureException();
      }         
   }

   public abstract bool IsReusable { get; }

   private static string CreateSignature(NameValueCollection parameters, string secret)
   {
      SortedDictionary<string, string> sortedParameters = new SortedDictionary<string, string>();
      foreach (string item in parameters.AllKeys)
      {
         sortedParameters.Add(item, parameters[item]);
      }
      return CreateSignature(sortedParameters, secret);
   }
   private static string CreateSignature(SortedDictionary<string, string> parameters, string secret)
   {         
      StringBuilder sb = new StringBuilder();
      foreach (KeyValuePair<string, string> kvp in parameters)
      {
         sb.Append(kvp.Key);
         sb.Append("=");
         sb.Append(kvp.Value);
      }
      sb.Append(secret);
      byte[] bytes = new MD5CryptoServiceProvider().ComputeHash(Encoding.UTF8.GetBytes(sb.ToString()));
      StringBuilder data = new StringBuilder();
      for (int i = 0; i < bytes.Length; ++i)
      {
         data.Append(bytes[ i ].ToString("x2"));
      }
      return data.ToString();
   }
}

Here we’ve just hardcoded the secret. If you’re targetting multiple client/users, you need to assign them an application id (most open web services call these an APIKey) and have it passed along with each query. You can then use it to lookup the appropriate secret, something like:

string apiKey = parameters["apikey"];
string secret = Account.GetSecretFromApiKey(apiKey);

And, that’s it :)

This entry was posted in Uncategorized. Bookmark the permalink. Follow any comments here with the RSS feed for this post.

3 Responses to Signing Requests (or anything) with Hashes

  1. Tigraine says:

    I just wanted to point out that going through the hassle of manually hashing and formatting the value to HEX can be avoided if you use:

    System.Web.Security.FormsAuthentication.HashPasswordForStoringInConfigFile(, );

    it will return a nicely formatted hash ready to be stored in a config file or passed to a browser :)

    Cool post btw, I’m just getting into HttpHandlers and love to see simple code out there :)

    greetings Daniel

  2. o.s. says:

    Karl very good post as usual. However, I think it’s very important to strike a 50/50 balance between formal and informal terms. Some readers may not realize that the ‘secret value’ mentioned in the post is actually whats called a ‘salt’ in cryptography. Although I really do want to point out that the use of informal common everyday terms can be extremely useful to teach others so its not really a problem its just that sometimes people may not make the connections. Good post and very interesting.

  3. FOR says:

    Can’t help myself..
    ..Must comment on you sample secret:
    …itsover9000 !!!!!!!!!!!!!!!!!!!!!!

    Thank you for the chuckle.
    (Oh and thanks for the rest of it too: good stuff !)