Friday, August 20, 2010

How to encode html in client side and benefit from request validation in asp.net in a case of html text input

It is common issue to have request validation failure while having HTML input in form inputs (TextBoxes). So easiest and widely used approach is to turn off request validation in the page to accept html input and encode only the expected control text. By ignoring other input controls we leave considerable space for attackers to inject some nasty html in to input controls.
So this approach is to have an input control which accepts html text in the page and also have request validation enabled. For this we can encode html in the client side and send only the encoded html to the server to comply with request validation in asp.net


Demo:
Tip: .net framwork validate inputs of asp.net controls only
<input type='text' runat='server id="txtName" /> - get validated
<asp:TextBox runat="server" Id="txtName" /> - get validated
It ignores regular input elements in a the page.
<input type='text '>  - not get validated
I have two side by side input controls one is text input and other is hiden field. From those two only the hidden field represents a .net control. You actually type on non .net control (html input). In the same time when I type it encodes html into hidden field (asp.net control) So even though I post two input controls when I post data one has row html(input element) and one has encoded html (asp.net control) .net framwork identify no risk as .net control (hidden filed) has encorded html. It just ignore the regular text input control with row html.
For a confirmation please see view source after row html postback of the demo page.
Example of use:
<%@ Register Assembly="ActiveTest" Namespace="ActiveTest" TagPrefix="asp" %> 
<%@ Page Language="C#" %>
<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head2" runat="server">
    <script type="text/javascript" src="http://code.jquery.com/jquery-latest.js"></script>
    <script runat="server">
        protected void Save(object sender, EventArgs e)
        {
            string encodedHtml = this.htxtDescrition.Text;
        }
    </script>
</head>
<body>
    <form id="form2" runat="server">
        Description <asp:HtmlTextBox runat="server" ID="htxtDescrition" IsMultiLine="true"  />
        <hr />
        <asp:Button runat="server" ID="Button1" Text="Save" OnClick="Save" />
    </form>
</body>
</html>
Control Implementaion
[PermissionSet(SecurityAction.Demand, Name = "FullTrust")]
public class HtmlTextBox : WebControlIPostBackDataHandlerINamingContainer
{
    public string Text
    {
        get { return (string)(ViewState["Text"] ?? (ViewState["Text"] = string.Empty)); }
        set { ViewState["Text"] = value; }
    }
    public int Rows
    {
        get { return (int)(ViewState["Rows"] ?? (ViewState["Rows"] = 10)); }
        set { ViewState["Rows"] = value; }
    }
    public int Colums
    {
        get { return (int)(ViewState["Colums"] ?? (ViewState["Colums"] = 40)); }
        set { ViewState["Colums"] = value; }
    }
    private string controlScript = @"
        function EncodeHtml(soruce) {
            var text = $(""#"" + soruce).val();
            var encodedHtml = $(""<div/>"").text(text).html();
            $(""#"" + soruce + ""Value"").val(encodedHtml);
        }
    ";
    private string instanceScript = @"
        $(document).ready(function () {{
            var controlId = ""#"" + ""{0}"" + ""Value"";
            var text = $(controlId).val();
            var encodedHtml = $(""<div/>"").text(text).html();
            $(controlId).val(encodedHtml);
        }});
    ";
    public bool IsMultiLine { getset; }
    public bool AutoPostBack { getset; }
    public event EventHandler TextChanged;
    public virtual bool LoadPostData(string postDataKey,
                                    NameValueCollection postCollection)
    {
        string postedValue = postCollection[postDataKey];
        this.Text = postedValue;
        return false;
    }
    public virtual void RaisePostDataChangedEvent()
    {
        OnCheckChanged(EventArgs.Empty);
    }
    protected virtual void OnCheckChanged(EventArgs e)
    {
        if (TextChanged != null)
            TextChanged(this, e);
    }
    protected override void Render(HtmlTextWriter output)
    {
        string colsAndRows = this.IsMultiLine ? string.Format(" rows=\"{0}\" cols=\"{1}\"",
            this.Rows, this.Colums) : string.Empty;
        output.Write(string.Format(@"
            <{0} type=""text"" id=""{1}"" {2}{3} 
            onchange=""javascript:EncodeHtml('{1}')""{4}>{5}</{0}>",
            this.IsMultiLine ? "textarea" : "input",
            this.ClientID,
            this.IsMultiLine ? string.Empty : string.Format(" value=\"{0}\""this.Text),
            this.AutoPostBack ?
                string.Format(" onclick=\"javascript:__doPostBack('{0}','');\""this.UniqueID) : string.Empty,
            colsAndRows,
            this.IsMultiLine ? this.Text : string.Empty));
        output.Write(string.Format("<input type='hidden' value='{0}' id='{1}Value' name='{2}' />",
            this.Text,
            this.ClientID,
            this.UniqueID));
    }
    protected override void OnPreRender(EventArgs e)
    {
        base.OnPreRender(e);
        if (this.Page != null)
        {
            this.Page.ClientScript.RegisterStartupScript(this.GetType(), this.ClientID,
                string.Format(this.instanceScript, this.ClientID), true);
            this.Page.ClientScript.RegisterClientScriptBlock(this.GetType(),
                this.GetType().Name, this.controlScript, true);
        }
    }
}

No comments:

Azure Storage Account Types

Defferent Types of Blobs Block blobs store text and binary data. Block blobs are made up of blocks of data that can be managed individually...