Sunday, February 12, 2012

How to encrypt and decrypt Query String Parameters in ASP.NET

Query strings are very easy way of passing data from one page to another page. The problem with query strings is that the information is visible in the url. However, many developers have been using this way carelessly including me and we are vulnerable to use the query string in some circumstances. In my experience, one friend of mine web site had attacked by hacker using SQL Injection method since he used the Inline SQL Statemnet and Query String in his programming. Hence query strings are able to see, we should encrypt the parameters where it is confidential or not so that we prevent our application from malicious access. In the following sample, I show you using RC2 encryption for the query strings when you click "Details" link on Gridview and it go to other page with encrypted query strings.

Create BasePage.cs as below.
using System;
using System.Collections.Generic;
using System.Web;
using System.Text;
using System.IO;
using System.Collections.Specialized;
using System.Security.Cryptography;
public class BasePage : System.Web.UI.Page
{
    // Key management for scrambling support
    public byte[] ScrambleKey
    {
        set
        {
            byte[] key = value;
            if (null == key)
            {
                // Use existing key if non provided
                key = ScrambleKey;
            }
            Session["ScrambleKey"] = key;
        }
        get
        {
            byte[] key = (byte[])Session["ScrambleKey"];
            if (null == key)
            {
                RC2CryptoServiceProvider rc2 = new RC2CryptoServiceProvider();
                rc2.GenerateKey();
                key = rc2.Key;
                Session["ScrambleKey"] = key;
            }
            return key;
        }
    }
    // Initialization vector management for scrambling support
    public byte[] ScrambleIV
    {
        set
        {
            byte[] key = value;
            if (null == key)
            {
                key = ScrambleIV;
            }
            Session["ScrambleIV"] = key;
        }
        get
        {
            byte[] key = (byte[])Session["ScrambleIV"];
            if (null == key)
            {
                RC2CryptoServiceProvider rc2 = new RC2CryptoServiceProvider();
                rc2.GenerateIV();
                key = rc2.IV;
                Session["ScrambleIV"] = key;
            }
            return key;
        }
    }
    public string Encrypt(string message)
    {
        UTF8Encoding textConverter = new UTF8Encoding();
        RC2CryptoServiceProvider rc2CSP = new RC2CryptoServiceProvider();

        //Convert the data to a byte array.
        byte[] toEncrypt = textConverter.GetBytes(message);

        //Get an encryptor.
        ICryptoTransform encryptor = rc2CSP.CreateEncryptor(ScrambleKey, ScrambleIV);

        //Encrypt the data.
        MemoryStream msEncrypt = new MemoryStream();
        CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write);

        //Write all data to the crypto stream and flush it.
        // Encode length as first 4 bytes
        byte[] length = new byte[4];
        length[0] = (byte)(message.Length & 0xFF);
        length[1] = (byte)((message.Length >> 8) & 0xFF);
        length[2] = (byte)((message.Length >> 16) & 0xFF);
        length[3] = (byte)((message.Length >> 24) & 0xFF);
        csEncrypt.Write(length, 0, 4);
        csEncrypt.Write(toEncrypt, 0, toEncrypt.Length);
        csEncrypt.FlushFinalBlock();

        //Get encrypted array of bytes.
        byte[] encrypted = msEncrypt.ToArray();

        // Convert to Base64 string
        string b64 = Convert.ToBase64String(encrypted);

        // Protect against URLEncode/Decode problem
        string b64mod = b64.Replace('+', '@');

        // Return a URL encoded string
        return HttpUtility.UrlEncode(b64mod);
    }
    public string Decrypt(string scrambledMessage)
    {
        UTF8Encoding textConverter = new UTF8Encoding();
        RC2CryptoServiceProvider rc2CSP = new RC2CryptoServiceProvider();
        // URL decode , replace and convert from Base64
        string b64mod = HttpUtility.UrlDecode(scrambledMessage);
        // Replace '@' back to '+' (avoid URLDecode problem)
        string b64 = b64mod.Replace('@', '+');
        // Base64 decode
        byte[] encrypted = Convert.FromBase64String(b64);

        //Get a decryptor that uses the same key and IV as the encryptor.
        ICryptoTransform decryptor = rc2CSP.CreateDecryptor(ScrambleKey, ScrambleIV);

        //Now decrypt the previously encrypted message using the decryptor
        // obtained in the above step.
        MemoryStream msDecrypt = new MemoryStream(encrypted);
        CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read);

        byte[] fromEncrypt = new byte[encrypted.Length - 4];

        //Read the data out of the crypto stream.
        byte[] length = new byte[4];
        csDecrypt.Read(length, 0, 4);
        csDecrypt.Read(fromEncrypt, 0, fromEncrypt.Length);
        int len = (int)length[0] | (length[1] << 8) | (length[2] << 16) | (length[3] << 24);

        //Convert the byte array back into a string.
        return textConverter.GetString(fromEncrypt).Substring(0, len);
    }
    public NameValueCollection DecryptQueryString(string scrambledMessage)
    {
        // Decode the query string
        string queryString = Decrypt(scrambledMessage);

        NameValueCollection result = new NameValueCollection();
        char[] splitChar = new char[] { '&' };
        char[] equalChar = new char[] { '=' };
        // Split query string to components
        foreach (string s in queryString.Split(splitChar))
        {
            // split each component to key and value
            string[] keyVal = s.Split(equalChar, 2);
            string key = keyVal[0];
            string val = String.Empty;
            if (keyVal.Length > 1) val = keyVal[1];
            // Add to the hashtable
            result.Add(key, val);
        }
        // return the resulting hashtable
        return result;
    }
}

Create a .aspx page and extend BasePage.cs we created before.
Put the following gridview that use ObjectDataSource and "Northwind" DB in <form> tag(for creating the below gridview and data access, you can see in the my previous post).
<asp:GridView ID="GridView2" runat="server" AutoGenerateColumns="False" CellPadding="4"
    AllowPaging="True" AllowSorting="True" AlternatingRowStyle-Wrap="True"
    DataSourceID="ObjectDataSource2" EmptyDataText="There is no data.">
    <RowStyle BackColor="#EFF3FB" />
    <Columns>
        <asp:BoundField HeaderText="No." DataField="No" />
        <asp:BoundField HeaderText="CustomerID" DataField="CustomerID" SortExpression="CustomerID"/>
        <asp:BoundField HeaderText="CompanyName" DataField="CompanyName" SortExpression="CompanyName" />
        <asp:BoundField HeaderText="ContactName" DataField="ContactName" SortExpression="ContactName" />
        <asp:TemplateField HeaderText="Action" >
            <ItemTemplate>
                 <asp:HyperLink ID="hlkdetail" runat="server"
                 NavigateUrl='<%# querystringencodedecode.aspx?query=" + Encrypt("ID=" + Eval("CustomerID"))%>'
                 Text="Detail"></asp:HyperLink>
            </ItemTemplate>
        </asp:TemplateField>
    </Columns>
    <AlternatingRowStyle BackColor="White" />
</asp:GridView>
<asp:ObjectDataSource ID="ObjectDataSource2" runat="server" SelectMethod="GridDataPage"
    TypeName="DAL" SelectCountMethod="DataRowCount"
    SortParameterName="SortExpression" EnablePaging="True">
    <SelectParameters>
        <asp:Parameter Name="maximumRows" Type="Int32" />
        <asp:Parameter Name="startRowIndex" Type="Int32" />
        <asp:Parameter Name="SortExpression" Type="String" />
    </SelectParameters>
</asp:ObjectDataSource>

Create querystringencodedecode.aspx and put the below label control to <form> tag.
<asp:Label ID="Label1" runat="server" Text="Label"></asp:Label>

Add the following code to Page_Load.
protected void Page_Load(object sender, EventArgs e)
{
    if (!string.IsNullOrEmpty(Request.QueryString["query"]))
    {
        System.Collections.Specialized.NameValueCollection querystring = DecryptQueryString(Request.QueryString["query"]);
        string id = querystring["ID"];
        //Handle id here.
        Label1.Text = id.ToString();
    }
}

Here are two snapshots to show how this example works.
Gridview page
When you click on Detail link, it go to below page with encrypted query string and then decrypt the query string again and show the parameter value on page.

querystringencodeDecode.aspx
You should avoid the query string method for passing data between pages when your data is important.

Original post : http://www.codeproject.com/kb/aspnet/ScrambleQueryStrings.aspx?fid=220116&df=90&mpp=25&noise=3&sort=Position&view=Quick&fr=26&display=Mobile&prof=False

No comments:

Post a Comment