Xerratus
Happily stressed out, since 1974


 
Wednesday, October 25, 2006
<< .NET 2.0 Global.asax code in separate file
Outlook 2003 Auto-Correct Anomaly >>

We've all been there.  We develop a web application that uses the querystring to pass data from a master list page to a detail page.  Usually just the ID is passed (just a good programming technique) and you even check and double check the value and type of data so that only the user who is supposed to see it, sees it.  But the temptation to change the id for the user is too great and nobody ever likes seeing any type of identifier in the querystring.

The solution I've developed simply encodes the querystring name/value pairs so that it is unreadable but can be retrieved and created with relative ease. 

Note: This is solution does not encrypt/decrypt the values.  Rather it uses Base64 encoding to render the string unreadable.  It is possible for somebody with knowledge to hack this but you're a good programmer... and you've solved for this, just as you did if a user simply changed the ID; right?

So back to the solution:

First, we need to create a simple class to encode and decode Base64 values. 

public sealed class Base64
{
    
public static string Encode(string data)
    {
        
try
        {
            
byte[] encData_byte = new byte[data.Length];
            encData_byte =
Encoding.UTF8.GetBytes(data);
            
string encodedData = Convert.ToBase64String(encData_byte);
            
return encodedData;
        }
        
catch(Exception e)
        {
            
throw new Exception("Error in base64Encode " + e.Message);
        }
    }

    
public static string Decode(string data)
    {
        
try
        {
            
UTF8Encoding encoder = new UTF8Encoding();
            
Decoder utf8Decode = encoder.GetDecoder();

            
byte[] todecode_byte = Convert.FromBase64String(data);
            
int charCount = utf8Decode.GetCharCount(todecode_byte, 0, todecode_byte.Length);
            
char[] decoded_char = new char[charCount];
            utf8Decode.GetChars(todecode_byte,
0, todecode_byte.Length, decoded_char, 0);
            
string result = new String(decoded_char);
            
return result;
        }
        
catch(Exception e)
        {
            
throw new Exception("Error in base64Decode " + e.Message);
        }
    }
}

Next, we create a QueryString class and give it an indexer to easily retrieve the values using an ordinal.  The getter returns null if the ordinal is empty, the querystring is not found or if the ordinal does not match up with any items found.  If Request.QueryString["q"] is found, we decode the string, split the pairs, loop thru each pair splitting the name/value out, and match on the ordinal.  If a match is found, we simply return the value of the item back.

public string this[string ordinal]
{
    
get
    {
        
if(ordinal == "")
        {
            
return null;
        }

        
if(HttpContext.Current.Request.QueryString["q"] == null)
        {
            
return null;
        }

        
// decodes to name/value pairs: id=1&something=what&test=true
        string decode;
        
try
        {
            decode =
Base64.Decode(HttpContext.Current.Request.QueryString["q"]);
        }
        
catch(Exception)
        {
            
return null;
        }

        
string[] pairs = decode.Split('&');

        
foreach(string pair in pairs)
        {
            
string[] item = pair.Split('=');

            
if(item[0].ToLower() == ordinal.ToLower())
            {
                
return item[1];
            }
        }

        
return null;
    }
}

For encoding, we supply a simple encode() method.

public string EncodePairs(string data)
{
    
if(data == "")
    {
        
return "";
    }

    
return Base64.Encode(data);
}

Now, for the UI:

To retrieve the querystring items, create an instance of QueryString (I chose to make it a disposable object so I could use the using pattern), check to see if the ordinal exists.  If it does, cast it to whatever datatype your system calls for.

// Retrieve the querystring values
using(QueryString q = new QueryString())
{
    IdLabel.Text = (q[
"id"] != null) ? q["id"] : "";
    NameLabel.Text = (q[
"name"] != null) ? q["name"] : "";
    ActiveLabel.Text = (q[
"active"] != null) ? q["active"] : "";
}

To encode pairs, build up the querystring like you normally would (leaving off the starting "?") and use the encode() method to encode it.  Then, append it to whatever page you are redirecting to as q=encodedstring. 

string pairs = String.Format("id={0}&name={1}&active={2}", IdTextBox.Text, NameTextBox.Text, ActiveRadioButtonList.SelectedValue);
string encoded;

// Set the querystring values
using(QueryString q = new QueryString())
{
    encoded = q.EncodePairs(pairs);
}

Response.Redirect(
"~/Default.aspx?q=" + encoded);

This is just a quick demonstration how one can use encoding to limit tampering when passing querystring data to another page.  There are many ways to achieve this without even using a querystring but for those instances where you either have to or want to, you now have another option. 

One last note:  This can be taken one step further if encryption is needed.  Just extend the QueryString class to encrypt/decrypt rather than encode/decode.  There are risks there as well but that's for you to decide.

Download the solution: QueryStringEncoding.zip (4.13 KB)