Apr 19th 2008 05:55 pm

Detecting and avoiding “A potentially dangerous Request.Form value was detected from the client” in asp.net

By default asp.net uses the setting validateRequests=”true”. This can lead to a paradox described here http://www.asp.net/learn/whitepapers/request-validation/. You can turn off request validation for your page or site, but that removes the basic protection asp.net provides against xss attacks. When you leave request validation on and a user enters text like “a<b”, the request will error out long before it reaches your actual code.

As the article asp.net article suggests, one should always htmlEncode and htmlDecode inputs posted to and rendered from the server. In our case, our own employees had discovered our failure to do this. They promptly exploited it to give custom styling to data items. This made the customers happy, triggered a nightmarish conflict between those who considered this a “feature” and those who knew it was a “bug”. Once the users took enough advantage of this loophole, it became entrenched.

When we finally got a chance to do a major refactor, our first thought was, “well, let’s do as the article suggests and put htmlDecode and htmlEncode on every input where we bind controls against values from our database.” - this seemed tedious and did nothing to prevent future developers from opening new xss holes as we developed new features.

Our next idea was use of asp.net control adapters to override the default asp.net controls and perform the htmlEncode and htmlDecode automatically. This way, developers wouldn’t have to remember to do it themselves. Unfortunately, control adapters only apply to markup (ascx/aspx) and not to controls created/added in the codebehind.

We finally opted to leave it the asp.net request validation on and:

  1. add validation to every input to prevent markup from being posted
  2. add validation in our business layer to prevent markup from entering our database
  3. strip out characters from user inputs that make it invalid
  4. use a third party control for entering rich text(markup). The control would handle the html encoding of the user input for us in these special cases and filter out invalid html tags.

It helps to work with super smart people, one of whom happened to write an a nice validation framework that used another developer’s excellent code gen., so steps 1 and 2 were pretty easy once we came up with the proper regex.

First we need to find out what validation asp.net is doing against form values. This code can be found using reflector in the System.Web.CrossSiteScripting class:

internal static bool IsDangerousString(string s, out int matchIndex)
{
    matchIndex = 0;
    int startIndex = 0;
    while (true) {
        int num2 = s.IndexOfAny(startingChars, startIndex);
        if (num2 < 0) {
            return false;
        }
        if (num2 == (s.Length - 1)) {
            return false;
        }
        matchIndex = num2;
        char ch = s[num2];
        if (ch != ‘&’) {
            if ((ch == ‘<’) && ((IsAtoZ(s[num2 + 1]) || (s[num2 + 1] == ‘!’)) || (s[num2 + 1] == ‘/’))) {
                return true;
            }
        } else if (s[num2 + 1] == ‘#’) {
            return true;
        }
        startIndex = num2 + 1;
    }
}

If a word begins with an “&#” or “<” preceding an alpha, “!” or “/” character, then the value is considered invalid by asp.net.

For part 1 we created a custom validator that utilized the following javascript function:

function NoXmlValidation(sender, args)
{
var re = new RegExp('(&#)|(<(?=(!|\\w|\\%|/)))', 'g');
args.IsValid = true;
if (args.Value && args.Value.match(re)) {
args.IsValid = false;
}
return;
}

Not all forms can be validated client side and prevent postback- specifically if you’re using validation groups and/or update panels, you can have major issues when the validators don’t fire and the postback generates the asp.net request validation error.
To work around this, we hooked into the ms client side application framework. When an html form is submitted we go through the inputs and strip out characters that violate our xss validation.

First the javascript code:

//called before onSubmit, validation should be called before, although the way we hooked into
//the event through the .net framework means that the code is also in there afterwards too
//but will never get called.
//This method goes through all the inputs and removes the characters that would cause a requestValidation exception on postback
//note: this actually modifies the element values, not just the data posted
function OnNormalSubmit() {
 // get all textarea and text inputs
 var inputs = $A(document.getElementsByTagName('input')).findAll(function(item) {return (item.type.toLowerCase() === 'text'); }).concat($A(document.getElementsByTagName('textarea')));
 var re = new RegExp('(&#)|(<(?=(!|\\w|\\%|/)))', 'g');
 // iterate through the all and replace invalid values
 inputs.each(
 		function(elt) {
 				elt.value=elt.value.replace(re,'');
 				if ( elt.Validators ) {
 					elt.Validators.each(
 						function(elt2) {
 							if ( elt2.isvalid !== undefined && !elt2.isvalid ) {
 								$(elt2).hide();
 							}
 						}
 					);
 				}
 			}
 	);
}

Note: the sample above uses the “$A“, “each“, and “findAll“ methods in the prototype library.  ”for” loops could be used instead, however, by using prototype we can abstract out the low level details and focus on the algorithm itself.

Now we need do is hook this method up to be called during the onPostback() method.
In your pagebase or master, put in following code:

Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
 If Not IsPostBack Then
 'register code for onSubmit validation to prevent requestValidationError. Note: must call ValidatorOnSubmit before calling custom code- otherwise the postback isn't prevented when the form is invalid.
 '.net puts our code before the standard ValidatorOnSubmit call, so we need to call it first manually.         

 ScriptManager.RegisterOnSubmitStatement(Me.Page, Me.GetType, "onSubmitScript", "if (typeof(ValidatorOnSubmit) === ""function"" && ValidatorOnSubmit() == false) return false; " + Microsoft.VisualBasic.vbCr + "if( self['OnNormalSubmit'] ) { OnNormalSubmit(); return true; }” + Microsoft.VisualBasic.vbCr)         

 End If
End Sub

No Comments yet »

Trackback URI | Comments RSS

Leave a Reply

« Detecting authentication cookie expiration on asyncronous requests | Concatenating values in t-sql select statements »