As a developer or security tester, it is important to know how built-in security mechanisms like EventValidation work. Starting with version 2.0 of the .NET Framework, Microsoft introduced the concept of “EventValidation” for validating PostBack data. The principal behind EventValidation is fairly simple -- if the framework sees certain values within a PostBack that it doesn’t know about, it throws an exception. The official explanation from MSDN is included below:
The event validation mechanism reduces the risk of unauthorized postback requests and callbacks. When the EnableEventValidation property is set to true, ASP.NET allows only the events that can be raised by the control during a postback request or callback.
From a security point of view, this sounds like a great feature. EventValidation actually does go a long way in protecting many non-editable application inputs (such as drop-down lists, etc) from parameter manipulation attacks.
Unfortunately there is very little documentation on how EventValidation works underneath the hood (you can find one attempt at an explanation here). One thing I have noticed is that there are some inconsistencies in how EventValidation gets enforced. Specifically, there are certain scenarios that seemingly *should* result in a validation exception but do not. Let’s look at a few…
The First Scenario
In the first scenario, we have an account maintenance page that allows users to view and potentially update their account information. Updates are submitted by pressing the “Update” image button on the account maintenance page. This button is a standard ASP.NET ImageButton control as shown by the code below:
<asp:ImageButton ID="ImageButton1" runat="server" OnClick="ImageButton1_Click" ImageUrl="/images/UpdateProfile.gif" />
As you can see, the image button triggers the ImageButton1_Click method within the codebehind when clicked, which will process the update. Based on application business rules, users within certain groups are not permitted to update their profile. As such, the developer took the following route to disable this feature:
ImageButton1.Enabled = false;
ImageButton1.ImageUrl = "/images/UpdateProfileDisabled.gif";
As you can see, if the user is in the “Guest” role, the button will be disabled and a grayed out image button will be displayed instead. For reference, the HTML generated by this code is shown below:
<input type="image" name="ImageButton1" id="Image1" disabled="disabled" src="/images/UpdateProfile.gif"/>
At this point, some of you are undoubtedly thinking about a possible attack here. The ImageButton control is disabled, but if the logic within the associated OnClick method (ImageButton1_Click) doesn’t perform the same role check, then the user can still submit a request to update their profile by forcing the request (even though the button has been disabled). Those of you familiar with how HTML works know that simply removing the disabled=”disabled” portion of the HTML INPUT tag shown above will enable the button within the browser. Once the enabled button is clicked, the ImageButton1_Click method will be invoked.
This is clearly bad coding on the part of the developer, however that is not the interesting part here. Based on the concept behind EventValidation, you would think that this scenario should throw an exception. In fact, if the control were a LinkButton instead of an ImageButton, a validation exception would be thrown. So why doesn’t EventValidation catch this? I’m not 100% certain but my guess is that it's because a LinkButton controls use the EventTarget parameter to pass in the “clicked” control name whereas with an ImageButton, the actual control name is passed as the name of an input parameter (along with its .x and .y coordinates) and the EventTarget is left blank.
Now let’s look at another scenario. This time the page uses the OnTextChanged event of a TextBox control to determine whether the value was modified and, if so, will update the database with the new value. As you can see from the excerpt below, the TBUserName_TextChanged method will be called when the TextBox value is changed.
<asp:TextBox ID="TBUserName" runat="server" OnTextChanged="TBUserName_TextChanged" />
Like the previous scenario, the developer uses a similar technique to prevent users outside of the “Administrators” role from updating their user name. If the user is not in the “Admins” role, the text box will be disabled within the browser so that the user will not be permitted to update the value.
TBUserName.Enabled = false;
Once again, the method associated with the OnTextChanged event (TBUserName_TextChanged) needs to also perform the same role check to prevent a malicious user from bypassing the “disabled” HTML attribute. This is another scenario where EventValidation should seemingly prevent such an attack from working since the control is not enabled, however it doesn’t.
Other Notable Points
Both of the scenarios outlined here will not be exploitable if the controls have their respective .Visible attributes set to false. Forcing a request that uses a non-visible control (one where .Visible is explicitly set to “false”) will generally always trigger an EventValidation exception. However, as you can see from the above two examples this is not always the case when only the .Enabled attribute is set to false.
A few other points of interest that I noticed with respect to the above scenarios:
- EventValidation DOES prevent users from submitting a LinkButton button when .Enabled is set to “false”
- The OnTextChanged event will not be raised when the .ReadOnly attribute on a TextBox control is set to “false”
So to summarize, Event Validation is a great feature of ASP.NET that can potentially thwart exploits where the developer may be incidentally relying on client-side controls (like the Browser) to prevent users from performing certain actions. Unfortunately, however, there is some inconsistency on how controls are treated when they are “Disabled” rather than not “Visible”.
The funny part is that based on the terminology alone, when asked if it is generally more secure to “Disable” something versus make it “Not Visible”, almost all security folks would recommend disabling. Ironic isn’t it?