Introduction

Query strings are a very convenient way of passing information to a new window or to a following page. The problem with query strings is that the information is visible. In my experience, the wiseass users see the query strings in the browser and try to duplicate the functionality: like "Oh, when I click the button, I get 'DeleteInventory.aspx?item=123&user=george&id=99' so, if I want to delete the item #222, I just need to use 'DeleteInventory.aspx?item=222&user=george&id=99' right?" Wrong, the id=99 is the database ID for the item to be removed. The rest of the information is for populating the label controls on the page. To avoid this problem, either use POST instead of GET (made much easier in ASP.NET 2.0) or scramble the information in the querystring. Nobody will make a mess with 'DeleteInventory.aspx?value=lkajhskjhsdfoiuhrt8974325lkjh'.

Solving the visible query string problem

Scrambling the query string allows you to pass information securely. The information displayed after the page URL is simply a jumble of characters. The query string information is scrambled prior to invoking the called page and is descrambled by the newly opened page. The scrambling and descrambling mechanism hides the information from the users while allowing you to continue using the convenient query string mechanism. The keys used to scramble and descramble the query strings are session-specific, allowing all the windows in a session to share information.

Why keep keys in session?

Many applications use a named window to perform a specific function. A user might have multiple browser windows referencing the same session (usually by using CTRL-N (Internet Explorer) to open new browser windows). All the windows in the session should scramble query strings the same way (using the same keys and initialization vectors) to allow the destination window to correctly decode the query string.

Scrambling and descrambling

The approach described here uses RC2 encryption to scramble (encrypt) and descramble (decrypt) the query string. Other algorithms such as ROT13 (simple replacement) DES and AES could be used as well. RC2 was chosen because it is relatively fast (efficiently implemented) and is well documented. Note that the purpose here is just to scramble (and later descramble) the query string. If you need to protect your information from malicious hackers, you should:

not be using RC2,

not put sensitive information in query strings.

Tamper-proofing your query strings

A secondary issue here is making sure the user cannot randomly modify the scrambled query string in a way that results in a valid, different, query string. Using a block cipher like RC2 solves this problem as the chance of a modified scrambled string passing decryption is very low. An Alternative approach is to embed a checksum (such as a CRC) in the string, when decoding the scrambled string verify the checksum to make sure the string was not modified.

Deriving from System.Web.Page

A simple approach to extend the web page functionality is to extend the System.Web.Page class. Instead of having your page inherit directly from System.Web.Page, inherit from a class (e.g. MyPage) derived from System.Web.Page. The added functionality is then made available to all web pages.

Handling keys and initialization vectors

The MyPage class defines the following methods and properties to handle algorithm keys and initialization vectors. (RC2 is a block cipher and requires both a key and an initialization vector to encode and decode information.)

As you can see above, the key and the initialization vector are generated on first access and stored in the session. I would not recommend this as a method for secure communication, but it is an acceptable solution for simple scrambling.

Scrambling and descrambling the query string

The Scramble(string message) method takes a string and returns a Base64 encoded scrambled string. Note that the original string length is prepended to the string – RC2 is a block cipher, without the length indicator you may get some padding at the end of the string after descrambling:

The helper method, DescrambleQueryString(string scrambledMessage) uses Descramble() to decode the query string and returns a NameValueCollection, allowing easy access to the query string values. Simply put, a NameValueCollection is a Hashtable with both the key and the value typed as strings. The NameValueCollection can be found in System.Collections.Specialized:

The URLDecode() problem

The URLDecode() method of System.Web.HttpUtility does not behave in an intuitive fashion when called repeatedly. For example: calling UrlEncode() with the string "abc+=" results in "abc%2b%3d". The first call to URLDecode() with "abc%2b%3d" correctly results in "abc+=". Calling UrlDecode() again with "abc+=" results in "abc =" - the '+' was replaced with a space. Why is this a problem at all? This is a problem because some .NET Framework methods silently call URLDecode(). My testing shows that the results from Response.QueryString[] is URL-decoded even though nothing is mentioned in the documentation. To avoid this problem, I recommend replacing all the '+' characters with '@' in the Base64 output string. The '@' character is URL safe and does not change when multiple calls are made to UrlDecode().

Author notes

If you can, always hide the information passed between pages. Use POST, keep things in Session or use Context. When dealing with legacy code where everything is passed as query strings, I recommend scrambling the information to protect your application from being misused.

Further work

The scrambling algorithm can be broken into a separate class and an interface can be defined allowing multiple algorithms to be used. I believe such an action would be overkill. All that the code is trying to do is to prevent a nosy user from modifying the query strings.

History

5/05 - Version 0.9

Used a Hashtable in the DescrambleQueryString() method.

9/05 - Version 1.0

Converted Hashtable to NameValueCollection.

10/05 - Version 1.1

Added URL encoding and decoding to protect Base64 string.

10/05 - Version 1.2

Added protection against multiple calls to URLDecode().

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

Share

About the Author

Giora Tamir has been Architecting, Designing and Developing software and hardware solutions for over 15 years. As an IEEE Senior member and a talented developer, Giora blends software development, knowledge of protocols, extensive understanding of hardware and profound knowledge of both Unix and Windows based systems to provide a complete solution for both defense and commercial applications. Giora, also known as G.T., now holds the position of Principal Engineer for ProfitLine, Inc. architecting the next generation of .NET applications based on a Service-Oriented-Architecture.

Gioras areas of interest include distributed applications, networking and cryptography in addition to Unix internals and embedded programming.

My guess would be bad session data. Before you login, you have one session and after logging in, you might have a new session created. The IV & KEY used by the algorithm are stored in Session, so if your session changes, the IV & KEY are wrong and the query string will not descramble correctly.

Yes. The encripted string is scrambled and then Base64'ed. Base64 output does not contain the '@' character, so substitution should be fine.
On the way back we substitute, then Base64-decode and then descramble - you can have as many '@'s as you like in the encoded string.