Peter's Gekko

Sponsors

The Lounge

News

Advertisement

Images in this post missing? We recently lost them in a site migration. We're working to restore these as you read this. Should you need an image in an emergency, please contact us at imagehelp@codebetter.com
Protecting an ASP.NET page against malicious input with ValidateRequest (A potentially dangerous Request.Form value was detected)

By default ASP.NET jumps through some hoops to protect your asp.net applications against malicious user input. It does this by scanning the data post back on tags which might contain unintended markup or even script. Take a page where the users enters something like

Now on postback asp.net will raise an exception

It suspects the <SCRIPT> piece of text. It will also suspect something like <B>

To prevent this you have to set the page directive validateRequest to false

Now all user input is accepted.

But you can still scan the user input for malicious input by using the ValidateInput() method of the Request. This methods validates three parts of the input

  • Form variables
  • QueryString
  • Cookies

It does work in a somewhat strange matter. At first sight nothing happens. But the moment you touch one of the parts it is validated. In case it does contain suspected input an HttpRequestValidationException exception is thrown. This snippet of code demonstrates how to work with ValidateInput.

        [Flags]
        public enum RequestValid
        {
            AllInValid = 0,
            FormValid = 1,
            QueryStringValid = 2,
            CookiesValid = 4
        }


        private RequestValid validateRequest()
        {
            RequestValid isValid = RequestValid.AllInValid;
            Request.ValidateInput();
            try
            {
                object touchForm = Request.Form;
                isValid = isValid | RequestValid.FormValid;
            }
            catch(HttpRequestValidationException)
            {
                // Take action
            }
            try
            {
                object touchQueryString = Request.QueryString;
                isValid = isValid | RequestValid.QueryStringValid;
            }
            catch(HttpRequestValidationException)
            {
                // Take action
            }
            try
            {
                object touchCookies = Request.Cookies;
                isValid = isValid | RequestValid.CookiesValid;
            }
            catch(HttpRequestValidationException)
            {
                // Take action
            }
            return isValid;
        }

 

You cannot influence what ValidateInput will scan for, that's hard coded. But it does issue a warning and you know what part of the input needs a closer investigation.


Posted 10-21-2005 3:02 PM by pvanooijen
Filed under:

[Advertisement]

Comments

Thomas Williams wrote Regular Expression to Prevent Users Entering Malicious (HTML) Form Data
on 12-07-2005 9:02 PM
Cross-site scripting (XSS) is a problem that ASP.NET helps you deal with by not allowing any &quot;malicious&quot;...
Joel Cable wrote re: Protecting an ASP.NET page against malicious input with ValidateRequest (A potentially dangerous Request.Form value was detected)
on 01-23-2006 11:16 AM
Very good. Where should this be this implemented? On Page Load? I suppose it could be done as soon as the request object is available.
pvanooijen wrote re: Protecting an ASP.NET page against malicious input with ValidateRequest (A potentially dangerous Request.Form value was detected)
on 01-24-2006 6:09 AM
That's PageLoad. Before that (in the init event) the request is not available yet.
gerard dsouza wrote re: Protecting an ASP.NET page against malicious input with ValidateRequest (A potentially dangerous Request.Form value was detected)
on 03-02-2007 5:57 AM

Hi,

i have a screen which captures data and saves it in a database. I use the Request.ValidateInput() function to validate data. In case there is an error i redirect it to a custom error screen. all this works fine. my problem arrises when i click the "Back" button on the error screen. The earlier screen is loaded again and the malicious data get saved even though i call the Request.ValidateInput() function on page_load as well as the save button click event. I do not want to dissable the back button in the browser. How do I fix this problem ??

pvanooijen wrote re: Protecting an ASP.NET page against malicious input with ValidateRequest (A potentially dangerous Request.Form value was detected)
on 03-02-2007 2:21 PM

You cannot disable the back button. Evenif you do so there is always the context menu back, the mouse button back or the page in the history. That's all client side behavior which is beyond your control.

But when the Back button brings you back to the page in error you still cannot post to that page. That will raise the exception again.

Thomas Williams Tech Blog wrote Regular Expression to Prevent Users Entering Malicious (HTML) Form Data
on 06-29-2008 7:17 AM

Regular Expression to Prevent Users Entering Malicious (HTML) Form Data

mq wrote re: Protecting an ASP.NET page against malicious input with ValidateRequest (A potentially dangerous Request.Form value was detected)
on 08-04-2008 7:05 PM

very nice. this helped me out with my problem. I could not catch this HttpRequestValidationException exception anywhere except in the Application_error but with this i can. Im using updatepanel with a textbox. any suggestions otherwise?

Warnar Boekkooi wrote re: Protecting an ASP.NET page against malicious input with ValidateRequest (A potentially dangerous Request.Form value was detected)
on 03-03-2009 8:58 AM

Hoi buur,

After looking into the potentially dangerous "problem" my self. I think this solution is great for catching the error.

But there is still the problem of what happens when you want to have some "potentially dangerous" information (like when you use a WYSIWYG editor)

Where is my fix:

/**

* Created By: Warnar Boekkooi

* Usage whin the page class:

public override void ProcessRequest(HttpContext context)

{

   //escape form for parts (because we trust them?)

   string[] unescapeFormParts = context.Request.escapeFormParts("txtMessage");

   this.PreInit += delegate { Page.Request.unescapeFormParts(unescapeFormParts); };

   //Continue proccessing request

   base.ProcessRequest(context);

}

**/

using System.Collections.Generic;

using System.Collections.Specialized;

using System.Reflection;

using System.Web;

using System.Linq;

using System.Web.UI;

namespace DashboardWiki.App_Library

{

   public static class HttpRequestEx

   {

       #region extention method's

       public static void EscapeFormParts(this Page page, params string[] fields)

       {

           //First call the collection to make sure it is filled

           NameValueCollection tmp = page.Request.Form;

           //escape form for parts (because we trust them?)

           string[] unescapeFormParts = escape(page.Request, "_form", fields);

           //Register unescape to the PreInit event

           page.PreInit += delegate { unescape(page.Request, "_form", unescapeFormParts); };

       }

       public static void EscapeQueryStringParts(this Page page, params string[] fields)

       {

           //First call the collection to make sure it is filled

           NameValueCollection tmp = page.Request.QueryString;

           //escape form for parts (because we trust them?)

           string[] unescapeFormParts = escape(page.Request, "_queryString", fields);

           //Register unescape to the PreInit event

           page.PreInit += delegate { unescape(page.Request, "_queryString", unescapeFormParts); };

       }

       #endregion

       #region the source of all evil

       private static string[] escape(HttpRequest request, string collectionVariable, IEnumerable<string> fields)

       {

           //Get the (private) version of the collection

           NameValueCollection col = (NameValueCollection)request.GetType().GetField(collectionVariable, BindingFlags.NonPublic | BindingFlags.Instance).GetValue(request);

           //Make collection writeable (by getting the readOnly property and setting it to false)

           PropertyInfo oReadable = col.GetType().GetProperty("IsReadOnly", BindingFlags.NonPublic | BindingFlags.Instance);

           oReadable.SetValue(col, false, null);

           //Fix values (By escaping them)

           List<string> changedKeys = new List<string>();

           foreach (string s in fields)

           {

               var requestKeys = from ck in col.AllKeys where ck.EndsWith("$" + s) select ck;

               foreach (string requestKey in requestKeys)

               {

                   col[requestKey] = HttpUtility.HtmlEncode(col[requestKey]);

                   changedKeys.Add(requestKey);

               }

           }

           //Make Form read only again

           oReadable.SetValue(col, true, null);

           //

           return changedKeys.ToArray();

       }

       private static void unescape(HttpRequest request, string collectionVariable, IEnumerable<string> formKeys)

       {

           //Get the (private) version of the collection

           NameValueCollection col = (NameValueCollection)request.GetType().GetField(collectionVariable, BindingFlags.NonPublic | BindingFlags.Instance).GetValue(request);

           //Make collection writeable (by getting the readOnly property and setting it to false)

           PropertyInfo oReadable = col.GetType().GetProperty("IsReadOnly", BindingFlags.NonPublic | BindingFlags.Instance);

           oReadable.SetValue(col, false, null);

           //Fix values (By escaping them)

           foreach (string formKey in formKeys)

           {

               col[formKey] = HttpUtility.HtmlDecode(col[formKey]);

           }

           //Make Form read only again

           oReadable.SetValue(col, true, null);

       }

       #endregion

   }

}

Add a Comment

(required)  
(optional)
(required)  
Remember Me?