OdeToCode IC Logo

ASP.NET Event Validation and “Invalid Callback Or Postback Argument” : Part II

Wednesday, March 22, 2006

In the last post we looked at the job event validation performs, and how we can trigger a validation error with some innocent JavaScript. If we receive exceptions because of event validation, we have to disable the feature, or learn how to work with the event validation.

Disable Event Validation

Pros: Easy to do
Cons: Less secure

Bertrand Le Roy wrote a post back in 2004: “Please, please, please, learn about injection attacks!”. Event validation is a feature designed to help prevent injection attacks (of course it can't prevent attacks all by itself).

We can disable event validation for an entire site in the web.config file.

<system.web>
   <
pages enableEventValidation="false"/>
</
system.web>

Alternatively, we can disable validation for a single page.

<%@ Page EnableEventValidation="false" ... %>

Register For Event Validation

Pros: Feel validated
Cons: We need to register all the legal post values before the page is sent to the client - this isn't always possible or practical

When we last left our application, we had a DropDownList with three legal values: “1”, “2”, or “3”. The problem was that client side script was adding a 4th option: “4”. If the user selected the 4th option and posted the form to the server, the server would throw an exception because it didn’t know “4” was a legal value.

The key to passing validation is to let ASP.NET know the value “4” is legal with the RegisterForEventValidation method of the ClientScriptManager class.

RegisterForEventValidation must be called during the rendering phase, so we need to override a Render method. We could start by overriding the Render method of our web form. Plug the following code right below the Page_Load method from the previous post.

protected override void Render(HtmlTextWriter writer)
{
   ClientScript.RegisterForEventValidation(
         _recipeList.UniqueID,
        
"4"
      );

  
base.Render(writer);
}

Remember event validation works by combining the hash of a control’s UniqueID property and a hash of each legal value for that control. The RegisterForEventValidation method accepts both of the calculation inputs as parameters. The method stores the result in a dictionary, and the page adds saves the dictionary as a hidden field on the client.

Adding the RegisterForEventValidation responsibility to the Page class is less than ideal, as the web form suddenly has to know about the DropDownList and its event validation problems. An alternate solution would be to build our own control.

using System;
using System.Web.UI.WebControls;
using System.Web.UI;

namespace OdeToCode.Web.UI.Controls
{
   [
SupportsEventValidation]
  
public class DynamicDropDownList : DropDownList
   {
      
protected override void Render(System.Web.UI.HtmlTextWriter writer)
      {
         Page.ClientScript.RegisterForEventValidation(
              
this.UniqueID,
              
"4"
            );
        
base.Render(writer);
      }
   }
}

A crucial step in the code is to add the SupportsEventValidation attribute to the class. If this attribute is not present, all the RegisterForEventValidation work is in vain. The runtime does not validate events for controls that do not have the SupportsEventValidation attribute present, nor does the runtime does look at the custom attributes of the control’s base class. The table of controls supporting event validation that appeared in the last post was output by a simple program that looped through all the Types in the System.Web assembly looking for the SupportsEventValidationAttribute.

In our example, we knew we only had one additional legal value – the value “4”. We need to call RegisterForEventValidation for every legal value the control might postback. This raises a couple issues because we may not know all of the legal values the control can take. AJAX might populate the control with values from a web service call that is bridged to a third party. Another issue is that the number of legal values might be extremely large.

Unfortunately, ASP.NET doesn’t expose a property to disable event validation for a single control – this would be a nice feature to have. However, if we create a custom control and leave off the SupportsEventValidation attribute, we’ll effectively disable event validation for instances of that class.