Attention: We have retired the ASP.NET Community Blogs. Learn more >

Understanding Request Validation in ASP.NET MVC 3

     Introduction:

 

          A fact that you must always remember "never ever trust user inputs". An application that trusts user inputs may be easily vulnerable to XSS, XSRF, SQL Injection, etc attacks. XSS and XSRF are very dangerous attacks. So to mitigate these attacks ASP.NET introduced request validation in ASP.NET 1.1. During request validation, ASP.NET will throw HttpRequestValidationException: 'A potentially dangerous XXX value was detected from the client', if he found, < followed by an exclamation(like <!) or < followed by the letters a through z(like <s) or & followed by a pound sign(like &#123) as a part of query string, posted form and cookie collection. In ASP.NET 4.0, request validation becomes extensible. This means that you can extend request validation. Also in ASP.NET 4.0, by default request validation is enabled before the BeginRequest phase of an HTTP request. ASP.NET MVC 3 moves one step further by making request validation granular. This allows you to disable request validation for some properties of a model while maintaining request validation for all other cases. In this article I will show you the use of request validation in ASP.NET MVC 3. Then I will briefly explain the internal working of granular request validation.

 

    Description:

 

          First of all create a new ASP.NET MVC 3 application. Then create a simple model class called MyModel,  

 

1 public class MyModel
2 {
3     public string Prop1 { getset; }
4  
5     public string Prop2 { get; set; }
6 }

 

          Then just update the index action method as follows,

 

1 public ActionResult Index(MyModel p)
2 {
3     return View();
4 }

 

          Now just run this application. You will find that everything works just fine. Now just append this query string ?Prop1=<s to the url of this application, you will get the HttpRequestValidationException exception.

          Now just decorate the Index action method with [ValidateInputAttribute(false)],

 

1 [ValidateInput(false)]
2 public ActionResult Index(MyModel p)
3 {
4     return View();
5 }

 

          Run this application again with same query string. You will find that your application run without any unhandled exception.

          Up to now, there is nothing new in ASP.NET MVC 3 because ValidateInputAttribute was present in the previous versions of ASP.NET MVC. Any problem with this approach? Yes there is a problem with this approach. The problem is that now users can send html for both Prop1 and Prop2 properties and a lot of developers are not aware of it. This means that now everyone can send html with both parameters(e.g, ?Prop1=<s&Prop2=<s). So ValidateInput attribute does not gives you the guarantee that your application is safe to XSS or XSRF. This is the reason why ASP.NET MVC team introduced granular request validation in ASP.NET MVC 3. Let's see this feature.


          Remove [ValidateInputAttribute(false)] on Index action and update MyModel class as follows,

 

1 public class MyModel
2 {
3     [AllowHtml]
4     public string Prop1 { getset; }
5  
6     public string Prop2 { get; set; }
7 }

 

          Note that AllowHtml attribute is only decorated on Prop1 property. Run this application again with ?Prop1=<s query string. You will find that your application run just fine. Run this application again with ?Prop1=<s&Prop2=<s query string, you will get HttpRequestValidationException exception. This shows that the granular request validation in ASP.NET MVC 3 only allows users to send html for properties decorated with AllowHtml attribute. 

          Sometimes you may need to access Request.QueryString or Request.Form directly. You may change your code as follows,

 

1 [ValidateInput(false)]
2 public ActionResult Index()
3 {
4     var prop1 = Request.QueryString["Prop1"];
5     return View();
6 }

 

          Run this application again, you will get the HttpRequestValidationException exception again even you have [ValidateInput(false)] on your Index action. The reason is that Request flags are still not set to unvalidate. I will explain this later. For making this work you need to use Unvalidated extension method,

 

 

1 public ActionResult Index()
2 {
3     var q = Request.Unvalidated().QueryString;
4     var prop1 = q["Prop1"];
5     return View();
6 }

 

          Unvalidated extension method is defined in System.Web.Helpers namespace . So you need to add using System.Web.Helpers; in this class file. Run this application again, your application run just fine.  

          There you have it. If you are not curious to know the internal working of granular request validation then you can skip next paragraphs completely. If you are interested then carry on reading.  

          Create a new ASP.NET MVC 2 application, then open global.asax.cs file and the following lines,  

 

1 protected void Application_BeginRequest()
2 {
3     var q = Request.QueryString;
4 }

 

          Then make the Index action method as, 

 

1 [ValidateInput(false)]
2 public ActionResult Index(string id)
3 {
4     return View();
5 }

 

          Please note that the Index action method contains a parameter and this action method is decorated with [ValidateInput(false)]. Run this application again, but now with ?id=<s query string, you will get HttpRequestValidationException exception at Application_BeginRequest method. Now just add the following entry in web.config,

 

1 <httpRuntime requestValidationMode="2.0"/>

 

          Now run this application again. This time your application will run just fine. Now just see the following quote from ASP.NET 4 Breaking Changes,

 

In ASP.NET 4, by default, request validation is enabled for all requests, because it is enabled before the BeginRequest phase of an HTTP request. As a result, request validation applies to requests for all ASP.NET resources, not just .aspx page requests. This includes requests such as Web service calls and custom HTTP handlers. Request validation is also active when custom HTTP modules are reading the contents of an HTTP request.

 

          This clearly state that request validation is enabled before the BeginRequest phase of an HTTP request. For understanding what does enabled means here, we need to see HttpRequest.ValidateInput, HttpRequest.QueryString and HttpRequest.Form methods/properties in System.Web assembly. Here is the implementation of HttpRequest.ValidateInput, HttpRequest.QueryString and HttpRequest.Form methods/properties in System.Web assembly,

 

 

01 public NameValueCollection Form
02 {
03     get
04     {
05         if (this._form == null)
06         {
07             this._form = new HttpValueCollection();
08             if (this._wr != null)
09             {
10                 this.FillInFormCollection();
11             }
12             this._form.MakeReadOnly();
13         }
14         if (this._flags[2])
15         {
16             this._flags.Clear(2);
17             this.ValidateNameValueCollection(this._form, RequestValidationSource.Form);
18         }
19         return this._form;
20     }
21 }
22  
23 public NameValueCollection QueryString
24 {
25     get
26     {
27         if (this._queryString == null)
28         {
29             this._queryString = new HttpValueCollection();
30             if (this._wr != null)
31             {
32                 this.FillInQueryStringCollection();
33             }
34             this._queryString.MakeReadOnly();
35         }
36         if (this._flags[1])
37         {
38             this._flags.Clear(1);
39             this.ValidateNameValueCollection(this._queryString, RequestValidationSource.QueryString);
40         }
41         return this._queryString;
42     }
43 }
44  
45 public void ValidateInput()
46 {
47     if (!this._flags[0x8000])
48     {
49         this._flags.Set(0x8000);
50         this._flags.Set(1);
51         this._flags.Set(2);
52         this._flags.Set(4);
53         this._flags.Set(0x40);
54         this._flags.Set(0x80);
55         this._flags.Set(0x100);
56         this._flags.Set(0x200);
57         this._flags.Set(8);
58     }
59 }

 

          The above code indicates that HttpRequest.QueryString and HttpRequest.Form will only validate the querystring and form collection if certain flags are set. These flags are automatically set if you call HttpRequest.ValidateInput method. Now run the above application again(don't forget to append ?id=<s query string in the url) with the same settings(i.e, requestValidationMode="2.0" setting in web.config and Application_BeginRequest method in global.asax.cs), your application will run just fine. Now just update the Application_BeginRequest method as,

 

1 protected void Application_BeginRequest()
2 {
3     Request.ValidateInput();
4     var q = Request.QueryString;
5 }

 

          Note that I am calling Request.ValidateInput method prior to use Request.QueryString property. ValidateInput method will internally set certain flags(discussed above). These flags will then tells the Request.QueryString (and Request.Form) property that validate the query string(or form) when user call Request.QueryString(or Request.Form) property. So running this application again with ?id=<s query string will throw HttpRequestValidationException exception. Now I hope it is clear to you that what does requestValidationMode do. It just tells the ASP.NET that not invoke the Request.ValidateInput method internally before the BeginRequest phase of an HTTP request if requestValidationMode is set to a value less than 4.0 in web.config. Here is the implementation of HttpRequest.ValidateInputIfRequiredByConfig method which will prove this statement(Don't be confused with HttpRequest and Request. Request is the property of HttpRequest class), 

 

01 internal void ValidateInputIfRequiredByConfig()
02 {
03     ...............................................................
04     ...............................................................
05     ...............................................................
06     ...............................................................
07     if (httpRuntime.RequestValidationMode >= VersionUtil.Framework40)
08     {
09         this.ValidateInput();
10     }
11 }

  

          Hopefully the above discussion will clear you how requestValidationMode works in ASP.NET 4. It is also interesting to note that both HttpRequest.QueryString and HttpRequest.Form only throws the exception when you access them first time. Any subsequent access to HttpRequest.QueryString and HttpRequest.Form will not throw any exception. Continuing with the above example, just update Application_BeginRequest method in global.asax.cs file as,

 

01 protected void Application_BeginRequest()
02 {
03     try
04     {
05         var q = Request.QueryString;
06         var f = Request.Form;
07     }
08     catch//swallow this exception
09     {
10     }
11     var q1 = Request.QueryString;
12     var f1 = Request.Form;
13 }

 

          Without setting requestValidationMode to 2.0 and without decorating ValidateInput attribute on Index action, your application will work just fine because both HttpRequest.QueryString and HttpRequest.Form will clear their flags after reading HttpRequest.QueryString and HttpRequest.Form for the first time(see the implementation of HttpRequest.QueryString and HttpRequest.Form above).

          Now let's see ASP.NET MVC 3 granular request validation internal working. First of all we need to see type of HttpRequest.QueryString and HttpRequest.Form properties. Both HttpRequest.QueryString and HttpRequest.Form properties are of type NameValueCollection which is inherited from the NameObjectCollectionBase class. NameObjectCollectionBase class contains _entriesArray, _entriesTable, NameObjectEntry.Key and NameObjectEntry.Value fields which granular request validation uses internally. In addition granular request validation also uses _queryString, _form and _flags fields, ValidateString method and the Indexer of HttpRequest class. Let's see when and how granular request validation uses these fields.

          Create a new ASP.NET MVC 3 application. Then put a breakpoint at Application_BeginRequest method and another breakpoint at HomeController.Index method. Now just run this application. When the break point inside Application_BeginRequest method hits then add the following expression in quick watch window, System.Web.HttpContext.Current.Request.QueryString. You will see the following screen, 

          

          GranularRequestValidationBeginRequest 

          

          Now Press F5 so that the second breakpoint inside HomeController.Index method hits. When the second breakpoint hits then add the following expression in quick watch window again, System.Web.HttpContext.Current.Request.QueryString. You will see the following screen, 

 

           GranularRequestValidationBeginRequest2

 

          First screen shows that _entriesTable field is of type System.Collections.Hashtable and _entriesArray field is of type System.Collections.ArrayList during the BeginRequest phase of the HTTP request. While the second screen shows that _entriesTable type is changed to Microsoft.Web.Infrastructure.DynamicValidationHelper.LazilyValidatingHashtable and _entriesArray type is changed to Microsoft.Web.Infrastructure.DynamicValidationHelper.LazilyValidatingArrayList during executing the Index action method. In addition to these members, ASP.NET MVC 3 also perform some operation on _flags, _form, _queryString and other members of HttpRuntime class internally. This shows that ASP.NET MVC 3 performing some operation on the members of HttpRequest class for making granular request validation possible.

          Both LazilyValidatingArrayList and LazilyValidatingHashtable classes are defined in the Microsoft.Web.Infrastructure assembly. You may wonder why their name starts with Lazily. The fact is that now with ASP.NET MVC 3, request validation will be performed lazily. In simple words, Microsoft.Web.Infrastructure assembly is now taking the responsibility for request validation from System.Web assembly. See the below screens. The first screen depicting HttpRequestValidationException exception in ASP.NET MVC 2 application while the second screen showing HttpRequestValidationException exception in ASP.NET MVC 3 application.

 

In MVC 2:

 

          ExceptionMVC2 

 

In MVC 3:

 

          ExceptionMVC3

 

          The stack trace of the second screenshot shows that Microsoft.Web.Infrastructure assembly (instead of System.Web assembly) is now performing request validation in ASP.NET MVC 3. Now you may ask: where Microsoft.Web.Infrastructure assembly is performing some operation on the members of HttpRequest class. There are at least two places where the Microsoft.Web.Infrastructure assembly performing some operation , Microsoft.Web.Infrastructure.DynamicValidationHelper.GranularValidationReflectionUtil.GetInstance method and Microsoft.Web.Infrastructure.DynamicValidationHelper.ValidationUtility.CollectionReplacer.ReplaceCollection method, Here is the implementation of these methods,

 

001 private static GranularValidationReflectionUtil GetInstance()
002 {
003     try
004     {
005         if (DynamicValidationShimReflectionUtil.Instance != null)
006         {
007             return null;
008         }
009         GranularValidationReflectionUtil util = new GranularValidationReflectionUtil();
010         Type containingType = typeof(NameObjectCollectionBase);
011         string fieldName = "_entriesArray";
012         bool isStatic = false;
013         Type fieldType = typeof(ArrayList);
014         FieldInfo fieldInfo = CommonReflectionUtil.FindField(containingType, fieldName, isStatic, fieldType);
015         util._del_get_NameObjectCollectionBase_entriesArray = MakeFieldGetterFunc<NameObjectCollectionBase, ArrayList>(fieldInfo);
016         util._del_set_NameObjectCollectionBase_entriesArray = MakeFieldSetterFunc<NameObjectCollectionBase, ArrayList>(fieldInfo);
017         Type type6 = typeof(NameObjectCollectionBase);
018         string str2 = "_entriesTable";
019         bool flag2 = false;
020         Type type7 = typeof(Hashtable);
021         FieldInfo info2 = CommonReflectionUtil.FindField(type6, str2, flag2, type7);
022         util._del_get_NameObjectCollectionBase_entriesTable = MakeFieldGetterFunc<NameObjectCollectionBase, Hashtable>(info2);
023         util._del_set_NameObjectCollectionBase_entriesTable = MakeFieldSetterFunc<NameObjectCollectionBase, Hashtable>(info2);
024         Type targetType = CommonAssemblies.System.GetType("System.Collections.Specialized.NameObjectCollectionBase+NameObjectEntry");
025         Type type8 = targetType;
026         string str3 = "Key";
027         bool flag3 = false;
028         Type type9 = typeof(string);
029         FieldInfo info3 = CommonReflectionUtil.FindField(type8, str3, flag3, type9);
030         util._del_get_NameObjectEntry_Key = MakeFieldGetterFunc<string>(targetType, info3);
031         Type type10 = targetType;
032         string str4 = "Value";
033         bool flag4 = false;
034         Type type11 = typeof(object);
035         FieldInfo info4 = CommonReflectionUtil.FindField(type10, str4, flag4, type11);
036         util._del_get_NameObjectEntry_Value = MakeFieldGetterFunc<object>(targetType, info4);
037         util._del_set_NameObjectEntry_Value = MakeFieldSetterFunc(targetType, info4);
038         Type type12 = typeof(HttpRequest);
039         string methodName = "ValidateString";
040         bool flag5 = false;
041         Type[] argumentTypes = new Type[] { typeof(string), typeof(string), typeof(RequestValidationSource) };
042         Type returnType = typeof(void);
043         MethodInfo methodInfo = CommonReflectionUtil.FindMethod(type12, methodName, flag5, argumentTypes, returnType);
044         util._del_validateStringCallback = CommonReflectionUtil.MakeFastCreateDelegate<HttpRequest, ValidateStringCallback>(methodInfo);
045         Type type = CommonAssemblies.SystemWeb.GetType("System.Web.HttpValueCollection");
046         util._del_HttpValueCollection_ctor = CommonReflectionUtil.MakeFastNewObject<Func<NameValueCollection>>(type);
047         Type type14 = typeof(HttpRequest);
048         string str6 = "_form";
049         bool flag6 = false;
050         Type type15 = type;
051         FieldInfo info6 = CommonReflectionUtil.FindField(type14, str6, flag6, type15);
052         util._del_get_HttpRequest_form = MakeFieldGetterFunc<HttpRequest, NameValueCollection>(info6);
053         util._del_set_HttpRequest_form = MakeFieldSetterFunc(typeof(HttpRequest), info6);
054         Type type16 = typeof(HttpRequest);
055         string str7 = "_queryString";
056         bool flag7 = false;
057         Type type17 = type;
058         FieldInfo info7 = CommonReflectionUtil.FindField(type16, str7, flag7, type17);
059         util._del_get_HttpRequest_queryString = MakeFieldGetterFunc<HttpRequest, NameValueCollection>(info7);
060         util._del_set_HttpRequest_queryString = MakeFieldSetterFunc(typeof(HttpRequest), info7);
061         Type type3 = CommonAssemblies.SystemWeb.GetType("System.Web.Util.SimpleBitVector32");
062         Type type18 = typeof(HttpRequest);
063         string str8 = "_flags";
064         bool flag8 = false;
065         Type type19 = type3;
066         FieldInfo flagsFieldInfo = CommonReflectionUtil.FindField(type18, str8, flag8, type19);
067         Type type20 = type3;
068         string str9 = "get_Item";
069         bool flag9 = false;
070         Type[] typeArray4 = new Type[] { typeof(int) };
071         Type type21 = typeof(bool);
072         MethodInfo itemGetter = CommonReflectionUtil.FindMethod(type20, str9, flag9, typeArray4, type21);
073         Type type22 = type3;
074         string str10 = "set_Item";
075         bool flag10 = false;
076         Type[] typeArray6 = new Type[] { typeof(int), typeof(bool) };
077         Type type23 = typeof(void);
078         MethodInfo itemSetter = CommonReflectionUtil.FindMethod(type22, str10, flag10, typeArray6, type23);
079         MakeRequestValidationFlagsAccessors(flagsFieldInfo, itemGetter, itemSetter, out util._del_BitVector32_get_Item, out util._del_BitVector32_set_Item);
080         return util;
081     }
082     catch
083     {
084         return null;
085     }
086 }
087  
088 private static void ReplaceCollection(HttpContext context, FieldAccessor<NameValueCollection> fieldAccessor, Func<NameValueCollection> propertyAccessor, Action<NameValueCollection> storeInUnvalidatedCollection, RequestValidationSource validationSource, ValidationSourceFlag validationSourceFlag)
089 {
090     NameValueCollection originalBackingCollection;
091     ValidateStringCallback validateString;
092     SimpleValidateStringCallback simpleValidateString;
093     Func<NameValueCollection> getActualCollection;
094     Action<NameValueCollection> makeCollectionLazy;
095     HttpRequest request = context.Request;
096     Func<bool> getValidationFlag = delegate {
097         return _reflectionUtil.GetRequestValidationFlag(request, validationSourceFlag);
098     };
099     Func<bool> func = delegate {
100         return !getValidationFlag();
101     };
102     Action<bool> setValidationFlag = delegate (bool value) {
103         _reflectionUtil.SetRequestValidationFlag(request, validationSourceFlag, value);
104     };
105     if ((fieldAccessor.Value != null) && func())
106     {
107         storeInUnvalidatedCollection(fieldAccessor.Value);
108     }
109     else
110     {
111         originalBackingCollection = fieldAccessor.Value;
112         validateString = _reflectionUtil.MakeValidateStringCallback(context.Request);
113         simpleValidateString = delegate (string value, string key) {
114             if (((key == null) || !key.StartsWith("__", StringComparison.Ordinal)) && !string.IsNullOrEmpty(value))
115             {
116                 validateString(value, key, validationSource);
117             }
118         };
119         getActualCollection = delegate {
120             fieldAccessor.Value = originalBackingCollection;
121             bool flag = getValidationFlag();
122             setValidationFlag(false);
123             NameValueCollection col = propertyAccessor();
124             setValidationFlag(flag);
125             storeInUnvalidatedCollection(new NameValueCollection(col));
126             return col;
127         };
128         makeCollectionLazy = delegate (NameValueCollection col) {
129             simpleValidateString(col[null], null);
130             LazilyValidatingArrayList array = new LazilyValidatingArrayList(_reflectionUtil.GetNameObjectCollectionEntriesArray(col), simpleValidateString);
131             _reflectionUtil.SetNameObjectCollectionEntriesArray(col, array);
132             LazilyValidatingHashtable table = new LazilyValidatingHashtable(_reflectionUtil.GetNameObjectCollectionEntriesTable(col), simpleValidateString);
133             _reflectionUtil.SetNameObjectCollectionEntriesTable(col, table);
134         };
135         Func<bool> hasValidationFired = func;
136         Action disableValidation = delegate {
137             setValidationFlag(false);
138         };
139         Func<int> fillInActualFormContents = delegate {
140             NameValueCollection values = getActualCollection();
141             makeCollectionLazy(values);
142             return values.Count;
143         };
144         DeferredCountArrayList list = new DeferredCountArrayList(hasValidationFired, disableValidation, fillInActualFormContents);
145         NameValueCollection target = _reflectionUtil.NewHttpValueCollection();
146         _reflectionUtil.SetNameObjectCollectionEntriesArray(target, list);
147         fieldAccessor.Value = target;
148     }
149 }

 

          Hopefully the above code will help you to understand the internal working of granular request validation. It is also important to note that Microsoft.Web.Infrastructure assembly invokes HttpRequest.ValidateInput method internally. For further understanding please see Microsoft.Web.Infrastructure assembly code. Finally you may ask: at which stage ASP NET MVC 3 will invoke these methods. You will find this answer by looking at the following method source,

 

  • Unvalidated extension method for HttpRequest class defined in System.Web.Helpers.Validation class.
  • System.Web.Mvc.MvcHandler.ProcessRequestInit method.
  • System.Web.Mvc.ControllerActionInvoker.ValidateRequest method.
  • System.Web.WebPages.WebPageHttpHandler.ProcessRequestInternal method.

 

    Summary:

 

          ASP.NET helps in preventing XSS attack using a feature called request validation. In this article, I showed you how you can use granular request validation in ASP.NET MVC 3. I explain you the internal working of  granular request validation. Hope you will enjoy this article too.

 

3 Comments

  • Excellent article.

  • Hi Imran, your post is very helpful in understanding the Request validation, can i validate property dynamically ?, i have requirement to add rich text editor dynamically in the page, so i don't have property in model to put AllowHtml attribute. i know control name in OnActionExecuting, is there anyway to validate false for particular control. any help is appreciated

  • @Naveen,
    For disabling all fields, you can use
    Controller.ValidateRequest=false at run time.
    For single property, set
    RequestValidationEnabled=false on metadata


Comments have been disabled for this content.