06 May 2009

Baffle, Befuddle, & Bewilder - Obscuring Url Tokens

Got a hankering to obscure the tokens passed along in a Url?

Say there's a business case for obscuring integer accountIDs like accountID=2201343077108877660.

What if the accountID in the fictitious Url below was better off unknown to the user?
http://bobtuse.blogspot.com?accountID=2201343077108877660
Security-through-Obscurity might be just the ticket. Encryption is useful to obscure the ID from the user. What if the accountID visible to the user was accountID=f2z64wFzyfK1HWsWzCayh1mn0Sjktl080?

That is, the url in the address bar was
http://bobtuse.blogspot.com?accountID=f2z64wFzyfK1HWsWzCayh1mn0Sjktl080
Following are C# code snippets of two methods you'll need. The TokenEncode method is
public static string TokenEncode(string input)
{
key = Encoding.UTF8.GetBytes(EncryptionKey.Substring(0, 8));
var des = new DESCryptoServiceProvider();
Byte[] inputByteArray = Encoding.UTF8.GetBytes(input);
var ms = new MemoryStream();
var cs = new CryptoStream(ms, des.CreateEncryptor(key, Iv), CryptoStreamMode.Write);
cs.Write(inputByteArray, 0, inputByteArray.Length);
cs.FlushFinalBlock();
return HttpServerUtility.UrlTokenEncode(ms.ToArray());
}

The TokenDecode method is
public static string TokenDecode(string input)
{
var inputByteArray = new Byte[input.Length];
key = Encoding.UTF8.GetBytes(EncryptionKey.Substring(0, 8));
var des = new DESCryptoServiceProvider();
inputByteArray = HttpServerUtility.UrlTokenDecode(input);
var ms = new MemoryStream();
var cs = new CryptoStream(ms, des.CreateDecryptor(key, Iv), CryptoStreamMode.Write);
cs.Write(inputByteArray, 0, inputByteArray.Length);
cs.FlushFinalBlock();
Encoding encoding = Encoding.UTF8;
return encoding.GetString(ms.ToArray());
}

Also, a few class level variables that must be visible are
private static readonly string EncryptionKey = "!962Oa#ji";
private static readonly byte[] Iv = {0x12, 0x34, 0x56, 0x78, 0x90, 0xAB, 0xCD, 0xEF};
private static byte[] key = {};


Usage

To obscure the accountID with TokenEncode, the C# to cobble together the Url might look like
long accountId = 2201343077108877660;
string url =
string.Format("http://bobtuse.blogspot.com?accountID={0}",
EncryptionHelper.TokenEncode(accountId.ToString()));

To unscramble the DES-crypto gibberish on the other end of a post with TokenDecode, the C# might look like
long accounId = Convert.ToInt64(EncryptionHelper.TokenDecode(Request.QueryString["ID"].Trim()));

Note that TokenEncode uses the .Net methods HttpServerUtility.UrlTokenEncode and TokenDecode uses HttpServerUtility.UrlTokenDecode. Some C# code snippets I tried used Convert.ToBase64String() and Convert.FromBase64String() instead. These convert methods work until the encoded token happens to have a +/ or = in it; these characters get munged in the query string and can't be de-coded on the post pick-up without using hack like string.replace for the errant cases.

The blog TipsForDevelopers has a good post about the Difference between Convert.ToBase64String and HttpServerUtility.UrlTokenEncode.

Paste these obfuscators into your quill. The next time you're called upon to Baffle, Befuddle, & Bewilder, TokenEncode and TokenDecode will be at your ready.