Click here to Skip to main content
Click here to Skip to main content

Global Query String Encryption

By , 26 Apr 2013
 

Introduction 

Most old web applications use query strings to send data between pages. This problem makes applications vulnerable to query string injection attacks. The main problem in these projects is that they cannot afford a lot of costs to change the way of using query strings.

Background 

In this project we are using HTTP Modules to encrypt and decrypt query strings.

Using the code  

The QueryStringModule class implements the IHttpModule interface. This interface uses the BeginRequest delegate to control requests to the website. As you can see in the picture above this module controls all of the requests and responses to the application.

In this project we use 64 bit encryption from the RijndaelManaged class. 

#region Using
 
using System;
using System.IO;
using System.Web;
using System.Text;
using System.Security.Cryptography;
 
#endregion
 
/// <summary>
/// Summary description for QueryStringModule
/// </summary>
public class QueryStringModule : IHttpModule
{
 
    #region IHttpModule Members
 
    public void Dispose()
    {
        // Nothing to dispose
    }
 
    public void Init(HttpApplication context)
    {
        context.BeginRequest += new EventHandler(context_BeginRequest);
    }
 
    #endregion
 
    private const string PARAMETER_NAME = "enc=";
    private const string ENCRYPTION_KEY = "key";
 
    void context_BeginRequest(object sender, EventArgs e)
    {
        HttpContext context = HttpContext.Current;
        if (context.Request.Url.OriginalString.Contains("aspx") && 
                   context.Request.RawUrl.Contains("?"))
        {
            string query = ExtractQuery(context.Request.RawUrl);
            string path = GetVirtualPath();
 
            if (query.StartsWith(PARAMETER_NAME, StringComparison.OrdinalIgnoreCase))
            {
                // Decrypts the query string and rewrites the path.
                string rawQuery = query.Replace(PARAMETER_NAME, string.Empty);
                string decryptedQuery = Decrypt(rawQuery);
                context.RewritePath(path, string.Empty, decryptedQuery);
            }
            else if (context.Request.HttpMethod == "GET")
            {
                // Encrypt the query string and redirects to the encrypted URL.
                // Remove if you don't want all query strings to be encrypted automatically.
                if (query != "")
                {
                    string encryptedQuery = Encrypt(query);
                    context.Response.Redirect(path + encryptedQuery);
                }
            }
        }
    }
 
    /// <summary>
    /// Parses the current URL and extracts the virtual path without query string.
    /// </summary>
    /// <returns>The virtual path of the current URL.</returns>
    private static string GetVirtualPath()
    {
        string path = HttpContext.Current.Request.RawUrl;
        path = path.Substring(0, path.IndexOf("?"));
        path = path.Substring(path.LastIndexOf("/") + 1);
        return path;
    }
 
    /// <summary>
    /// Parses a URL and returns the query string.
    /// </summary>
    /// <param name="url" />The URL to parse.
    /// <returns>The query string without the question mark.</returns>
    private static string ExtractQuery(string url)
    {
        int index = url.IndexOf("?") + 1;
        return url.Substring(index);
    }
 
    #region Encryption/decryption
 
    /// <summary>
    /// The salt value used to strengthen the encryption.
    /// </summary>
    private readonly static byte[] SALT = Encoding.ASCII.GetBytes(ENCRYPTION_KEY.Length.ToString());
 
    /// <summary>
    /// Encrypts any string using the Rijndael algorithm.
    /// </summary>
    /// <param name="inputText" />The string to encrypt.
    /// <returns>A Base64 encrypted string.</returns>
    public static string Encrypt(string inputText)
    {
        RijndaelManaged rijndaelCipher = new RijndaelManaged();
        byte[] plainText = Encoding.Unicode.GetBytes(inputText);
        PasswordDeriveBytes SecretKey = new PasswordDeriveBytes(ENCRYPTION_KEY, SALT);
 
        using (ICryptoTransform encryptor = 
          rijndaelCipher.CreateEncryptor(SecretKey.GetBytes(32), SecretKey.GetBytes(16)))
        {
            using (MemoryStream memoryStream = new MemoryStream())
            {
                using (CryptoStream cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write))
                {
                    cryptoStream.Write(plainText, 0, plainText.Length);
                    cryptoStream.FlushFinalBlock();
                    return "?" + PARAMETER_NAME + Convert.ToBase64String(memoryStream.ToArray());
                }
            }
        }
    }
 
    /// <summary>
    /// Decrypts a previously encrypted string.
    /// </summary>
    /// <param name="inputText" />The encrypted string to decrypt.
    /// <returns>A decrypted string.</returns>
    public static string Decrypt(string inputText)
    {
        RijndaelManaged rijndaelCipher = new RijndaelManaged();
        byte[] encryptedData = Convert.FromBase64String(inputText);
        PasswordDeriveBytes secretKey = new PasswordDeriveBytes(ENCRYPTION_KEY, SALT);
 
        using (ICryptoTransform decryptor = 
          rijndaelCipher.CreateDecryptor(secretKey.GetBytes(32), secretKey.GetBytes(16)))
        {
            using (MemoryStream memoryStream = new MemoryStream(encryptedData))
            {
                using (CryptoStream cryptoStream = 
                  new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read))
                {
                    byte[] plainText = new byte[encryptedData.Length];
                    int decryptedCount = cryptoStream.Read(plainText, 0, plainText.Length);
                    return Encoding.Unicode.GetString(plainText, 0, decryptedCount);
                }
            }
        }
    }
 
    #endregion
}

Remember to add this line to the Web.config to make this module work:

<system.web>
  <httpmodules>
          <add type="QueryStringModule" name="QueryStringModule"/>
  </httpmodules>
</system.web>

For debugging purposes, if you need to disable this feature you should clear the add tag in httpmodules in web.config.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Farhad Hazraty Eini
Software Developer SoftwareUUT
Iran (Islamic Republic Of) Iran (Islamic Republic Of)
Member
Software Engineer of Urmia University of Technology (دانشگاه صنعتی ارومیه),
MCTS in Mojtame Fanni Tehran (MFT),
 
Worked with :
C#,
C++,
ASP.net,
SQL Server
 
My Server Solution Blog

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
Hint: For improved responsiveness ensure Javascript is enabled and choose 'Normal' from the Layout dropdown and hit 'Update'.
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
QuestionMissing downloadstaffSmitha Vijayan26 Apr '13 - 6:02 
AnswerRe: Missing downloadmemberFarhad Hazraty Eini26 Apr '13 - 6:55 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web03 | 2.6.130513.1 | Last Updated 26 Apr 2013
Article Copyright 2013 by Farhad Hazraty Eini
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid