Globally Handling Request Validation In ASP.NET MVC

   Introduction:

          Cross Site Scripting(XSS) and Cross-Site Request Forgery (CSRF) attacks are one of dangerous attacks on web.  They are among the most famous security issues affecting web applications. OWASP regards XSS is the number one security issue on the Web. Both ASP.NET Web Forms and ASP.NET MVC paid very much attention to make applications build with ASP.NET as secure as possible. So by default they will throw an exception 'A potentially dangerous XXX value was detected from the client', when they see, < 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 querystring, posted form and cookie collection. This is good for lot of applications. But this is not always the case. Many applications need to allow users to enter html tags, for example applications which uses  Rich Text Editor. You can allow user to enter these tags by just setting validateRequest="false" in your Web.config application configuration file inside <pages> element if you are using Web Form. This will globally disable request validation. But in ASP.NET MVC request handling is different than ASP.NET Web Form. Therefore for disabling request validation globally in ASP.NET MVC you have to put ValidateInputAttribute in your every controller. This become pain full for you if you have hundred of controllers. Therefore in this article i will present a very simple way to handle request validation globally through web.config.

   Description:

          Before starting how to do this it is worth to see why validateRequest in Page directive and web.config not work in ASP.NET MVC. Actually request handling in ASP.NET Web Form and ASP.NET MVC is different. In Web Form mostly the HttpHandler is the page handler which checks the posted form, query string and cookie collection during the Page ProcessRequest method, while in MVC request validation occur when ActionInvoker calling the action. Just see the stack trace of both framework.

   ASP.NET MVC Stack Trace:

 

   System.Web.HttpRequest.ValidateString(String s, String valueName, String collectionName) +8723114
   System.Web.HttpRequest.ValidateNameValueCollection(NameValueCollection nvc, String collectionName) +111
   System.Web.HttpRequest.get_Form() +129
   System.Web.HttpRequestWrapper.get_Form() +11
   System.Web.Mvc.ValueProviderDictionary.PopulateDictionary() +145
   System.Web.Mvc.ValueProviderDictionary..ctor(ControllerContext controllerContext) +74
   System.Web.Mvc.ControllerBase.get_ValueProvider() +31
   System.Web.Mvc.ControllerActionInvoker.GetParameterValue(ControllerContext controllerContext, ParameterDescriptor parameterDescriptor) +53
   System.Web.Mvc.ControllerActionInvoker.GetParameterValues(ControllerContext controllerContext, ActionDescriptor actionDescriptor) +109
   System.Web.Mvc.ControllerActionInvoker.InvokeAction(ControllerContext controllerContext, String actionName) +399
   System.Web.Mvc.Controller.ExecuteCore() +126
   System.Web.Mvc.ControllerBase.Execute(RequestContext requestContext) +27

  

ASP.NET Web Form Stack Trace:

 


   System.Web.HttpRequest.ValidateString(String s, String valueName, String collectionName) +3213202
   System.Web.HttpRequest.ValidateNameValueCollection(NameValueCollection nvc, String collectionName) +108
   System.Web.HttpRequest.get_QueryString() +119
   System.Web.UI.Page.GetCollectionBasedOnMethod(Boolean dontReturnNull) +2022776
   System.Web.UI.Page.DeterminePostBackMode() +60
   System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +6953
   System.Web.UI.Page.ProcessRequest(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +154
   System.Web.UI.Page.ProcessRequest() +86

           

           Since the first responder of request in ASP.NET MVC is the controller action therefore it will check the posted values during calling the action. That's why web.config's requestValidate not work in ASP.NET MVC.

            So let's see how to handle this globally in ASP.NET MVC. First of all you need to add an appSettings in web.config.

 


    

 

            I am using the same key used in disable request validation in Web Form. Next just create a new ControllerFactory by derving the class from DefaultControllerFactory.

 

public class MyAppControllerFactory : DefaultControllerFactory
    {
        protected override IController GetControllerInstance(Type controllerType)
        {
            var controller = base.GetControllerInstance(controllerType);
            string validateRequest=System.Configuration.ConfigurationManager.AppSettings["validateRequest"];
            bool b;
            if (validateRequest != null && bool.TryParse(validateRequest,out b))
                ((ControllerBase)controller).ValidateRequest = bool.Parse(validateRequest);
            return controller;
        }
    }

            

            Next just register your controller factory in global.asax.

 

     protected void Application_Start()
        {
            //............................................................................................
            ControllerBuilder.Current.SetControllerFactory(new MyAppControllerFactory());
        }

              This will prevent the above exception to occur in the context of ASP.NET MVC. But if you are using the Default WebFormViewEngine then you need also to set validateRequest="false" in your web.config file inside <pages> element

            Now when you run your application you see the effect of validateRequest appsetting. One thing also note that the ValidateInputAttribute placed inside action or controller will always override this setting.

 

   Summary:

          Request validation is great security feature in ASP.NET but some times there is a need to disable this entirely. So in this article i just showed you how to disable this globally in ASP.NET MVC. I also explained the difference between request validation in Web Form and ASP.NET MVC. Hopefully you will enjoy this.

3 Comments

Comments have been disabled for this content.