Saturday, November 17, 2007

Event Validation in ASP.NET

Recently at work, we ran into an issue where we needed to turn Page.EnableEventValidation on in our application.  Yes, would you believe it, we had to turn it ON.  Why did we have it off, you ask?  Because when we migrated our app from 1.1 to 2.0, we didn't have the time to diagnose and fix the issues caused by leaving the event validation on.

Not only didn't we have the time to find out what was causing it, we didn't have the time to do the research as to what event validation is, and why it's there.  Well, 2 years later, and I've made time to figure out what it is.

The purpose of event validation is to ensure that the posted value(s) of select lists are values that came from the application, and aren't something that has been injected into the page using malicious client script.

The example below shows how to test to see how event validation behaves.  What I do is add a drop down list to the page, then through Java Script, I dynamically add a value to the drop down list.

<%@ Page Language="C#" %>
 
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 
<script runat="server">
 
</script>
 
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>Untitled Page</title>
</head>
<body>
    <form id="form1" runat="server">
        <div>
            <asp:DropDownList ID="myDropDownList" runat="Server" AutoPostBack="true">
                <asp:ListItem />
            </asp:DropDownList>
        </div>
    </form>
 
    <script language="javascript" type="text/javascript">
        var dropDownList = document.getElementById('<%= myDropDownList.ClientID %>');
        dropDownList.options[1] = new Option('Text 1', 'Value1');
    </script>
 
</body>
</html>

When I choose the item from the drop down list that was added dynamically, I get the following error:

Screen Shot of Error

Under the covers, what happens is that ASP.NET makes a hash of all of the values that exist for the drop down list while the page is still on the server, and then stores that hash in a hidden input on the page.

<input type="hidden" name="__EVENTVALIDATION"
            id="__EVENTVALIDATION"
            value="/wEWAwKp1OaKAQLPw96oCwLPw96oC/Pix6BjqjWDWDMfBB7u0UhhYz8u" />

When the page posts back, it verifies the values posted, with what exists in that hash.  If it can't find it, and you have event validation turned on, then you'll get the exception.

Now, the above example is a very reasonable thing to do.  However, if you want to do it, you've only go two options when it comes to event validation. 

The first option is to turn off event validation for the page.  Not the best plan, because now it's up to you to make sure that you're values are validated. 

The second option is to tell the server all of the available values you're going to potentially be adding dynamically.  To tell the server the possible values, you use the ClientScript.RegisterForEventValidation method.  Again, not a perfect option, as some times you'll be able to identify these values, but sometimes you won't be able to anticipate all those values (AJAX'ified drop down list for instance).

Below is the same page above, but it uses the ClientScript.RegisterForEventValidation method in the overridden Render method (this method can only called during the render phase of the page lifecycle).

<%@ Page Language="C#" %>
 
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 
<script runat="server">
    protected override void Render(HtmlTextWriter writer)
    {
        ClientScript.RegisterForEventValidation(myDropDownList.UniqueID, "Value1");
        base.Render(writer);
    }
</script>
 
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>Untitled Page</title>
</head>
<body>
    <form id="form1" runat="server">
        <div>
            <asp:DropDownList ID="myDropDownList" runat="Server" AutoPostBack="true">
                <asp:ListItem />
            </asp:DropDownList>
        </div>
    </form>
 
    <script language="javascript" type="text/javascript">
        var dropDownList = document.getElementById('<%= myDropDownList.ClientID %>');
        dropDownList.options[1] = new Option('Text 1', 'Value1');
    </script>
 
</body>
</html>