1. Original Entry + Comments2. Write a Comment3. Preview Comment


January 23, 2012  |  Need feedback: draft article about request validation  |  2660 hit(s)

A couple of us on the ASP.NET documentation team are working on a topic that describes how to disable request validation in ASP.NET. What do you think about the following draft? Any feedback welcome.



Request validation is a feature in ASP.NET that examines an HTTP request and determines whether it contains potentially dangerous content. In this context, potentially dangerous content is any HTML markup or JavaScript code in the body, header, query string, or cookies of the request. ASP.NET performs this check because markup or code found in the URL query string, cookies, or posted form values might have been added to the request for malicious purposes.

For example, if your site has a form where users enter comments, a malicious user could enter JavaScript code in a <script> element. Then when you display the page with the comment to other users, the browser would execute that JavaScript code as if it had been generated by your website. This is commonly referred to as a cross-site scripting (XSS) attack.

Request validation helps prevent this kind of attack. If ASP.NET detects any markup or code in a request, it throws a "potentially dangerous value was detected " error and stops page processing. The default error page looks like this (click to enlarge):



Request validation throws this exception when any HTML markup is detected, including harmless markup like <b> (bold) elements. This can be a problem if you want your application to accept HTML markup. For example, if your site lets users add comments, you might want to let users perform basic formatting using HTML tags that put text in bold or italics. In cases like these, you can disable request validation and check for malicious content manually, or you can customize request validation so that certain kinds of markup or script are accepted. For information about how to specify a custom error page instead of the default page shown above, see How to: Handle Application-Level Errors. For information about how to customize request validation, see the whitepaper Security Extensibility in ASP.NET 4 (PDF).

Disabling Request Validation

Security Note  If you disable request validation, you must check the user input yourself for dangerous HTML or JavaScript. For more information, see Manually Checking Requests later in this topic.

The method that you use to disable request validation depends on what type of ASP.NET web application you are working with:

Disabling Request Validation in ASP.NET Web Forms (4.0 or later)

You can disable request validation for an entire application, but this is not recommended. The recommendation is to selectively disable request validation only for the virtual paths or specific pages where you want to allow markup.

In either case, you must make two changes in the Web.config file. The first change is to set the requestValidationMode attribute of the <httpRuntime> element to "2.0". This makes request validation occur later in the sequence of request processing events. This setting is required for applications using ASP.NET 4 and later because for as of ASP.NET 4, request validation takes place earlier in the request life cycle than it did in previous versions of ASP.NET.
<system.web>
<httpRuntime requestValidationMode="2.0" />
</system.web>
The following example shows how to make request validation occur later for a single page, in this case the Test.aspx page:
<location path="test.aspx">
<system.web>
<httpRuntime requestValidationMode="2.0" />
</system.web>
</location>
The second change is to set validationRequest to false. For the application as a whole, you do this using the <pages> element in the Web.config file as shown in the following example. (This setting is effective only if you also set requestValidationMode="2.0".)
<configuration>
<system.web>
<pages validateRequest="false" />
</system.web>
</configuration>
For an individual page, you can set set validationRequest to false in the @ Page directive of the page, like this:
<@ Page validateRequest="false" %>

Disabling Request Validation in ASP.NET MVC

To disable request validation in an ASP.NET MVC application, you must change request validation to occur earlier in the sequence of request processing as explained earlier for ASP.NET Web Forms. In the Web.config file, make the following setting:
<system.web>
<httpRuntime requestValidationMode="2.0" />
</system.web>
In ASP.NET MVC, you can disable request validation for an action method, for a property, or for a field (input element) in a request. If you disable validation for an action method, you disable it for any requests that invoke that method — that is, all user input is allowed for any request that calls the action method. This is therefore the least secure way to disable request validation.

If you disable validation for a property, you allow user input for any reference to that property. If you disable validation for specific fields, you can control which request element (field) allows arbitrary user input.

To disable request validation for an action method, mark the method with the attribute ValidateInput(false), as shown in the following example:
[HttpPost]
[ValidateInput(false)]
public ActionResult Edit(string comment)
{
if (ModelState.IsValid)
{
// Etc.
}
return View(comment);
}
To disable request validation for a specific property, mark the property definition with the AllowHtml attribute:
[AllowHtml]
public string Prop1 { get; set; }
To disable request validation for a specific field in a request (for example, for an input element or query string value), call the System.Web.Helpers.Unvalidated method when you get the item, as shown in the following example:
var rawComment = Request.Unvalidated().Form["comment"];

Disabling Request Validation in ASP.NET Web Pages

To disable request validation for ASP.NET Web Pages, in code, call the Request.Unvalidated method and pass it the name of the field or other object that you want to bypass request validation for. The comments in the following code snippet indicate which lines of code cause request validation to be invoked and which ones do not.

Note  In ASP.NET Web Pages applications that do not also include Web Forms pages or MVC controllers, you do not need to make any changes in the Web.config file.
// Validated, throws error if input includes markup
var userComment = Request.Form["userInput"];

Request.Unvalidated("userInput"); // Validation bypassed
Request.Unvalidated().Form["userInput"]; // Validation bypassed

Request.QueryString["userPreference"]; // Validated
Request.Unvalidated().QueryString["userPreference"]; // Validation bypassed;

Manually Checking Requests

If you disable request validation, you must manually check the unvalidated user input for potentially dangerous input. Doing this is critical for the security of your application. However, it is not necessarily an easy task. If your code is flawed or if you forget to protect one page or one field, malicious users may eventually find and exploit that weakness.

In general, you should restrict as narrowly as possible the list of HTML tags that you will accept, and reject everything else. (This is sometimes referred as using a whitelist.)

If you are working with Web Forms pages, you can often use a third-party "rich text" control that lets users format text. These controls often have validation routines built in that permit only safe HTML. (If you use a control like this, make sure that it offers HTML safety.)

If you are not using a control like that, a very simple approach is to HTML-encode the user input, and then to selectively unencode just the HTML tags that you want to allow. This is practical if the tags you want to allow do not include attributes, such as <b>, <strong>, <i>, and <em>. The following example shows a way to encode and then selectively decode just the <b> and <i> tags.
// Encode the string input
StringBuilder sb = new StringBuilder(
HttpUtility.HtmlEncode(htmlInputTxt.Text));
// Selectively allow <b> and <i>
sb.Replace("&<b&>", "<b>");
sb.Replace("&</b&>", "</b>");
sb.Replace("&<i&>", "<i>");
sb.Replace("&</i&>", "</i>");
To allow more flexible HTML markup in the user input, you can use third-party libraries, such as the HTML Agility Pack that you can download from the CodePlex website, or the open-source OWASP Anti-Samy utility. For more information, see the example on the OWASP site of disabling request validation for ASP.NET.

Another approach is to use an alternative form of markup, like MarkDown, and then convert the user's text to valid and safe HTML. Many wikis use this approach. For more information about Markdown, see the Daring Fireball site.

See Also

New ASP.NET Request Validation Features for ASP.NET 4.5
ASP.NET MVC Tip #48 – Disable Request Validation




Kent Sharkey   23 Jan 12 - 7:51 PM

Random, probably worthless feedback:

For an individual page, you can set set validationRequest to false in the @ Page directive of the page, like this:

It might not be entirely clear if the requestValidationMode is still required if you do this.

It might be useful to do a bit more in-depth version of the "HTML-encode the user input" section, or show/link a bbcode-esque demo app.

The page is already long enough, but I wonder if discussion of the encoding colon (there is medication for that, I hear) is worthwhile here, or if you're only caring about input at this point.

Love the callout to Markdown.


 
Alan   25 Jan 12 - 4:34 PM

Can I capture the "potentially dangerous value" exception and handle it myself? What I'd like to do is capture the exception then direct the user back to the same page with an appropriate error message. The article points to a pdf (which I didn't read) that tells me how to customize security validation, maybe it's there. A simple example would round out this otherwise fine exposition.

 
mike   28 Jan 12 - 12:09 PM

@Kent and @Alan, thanks for your feedback. And I apologize for the delay in responding; I posted this and then ran off on vacation, which in retrospect was sort of a dumb thing to do. In response:

@Kent -- I confess that I do not know what you mean by the "encoding colon." Details?

@Alan -- let me look into this. I suspect this might be difficult, given that in .NET 4 and beyond, the request is validated very early in the pipeline. But that's just idle speculation on my part. I'll get you a proper answer Real Soon.


 
mike   29 Jan 12 - 12:01 PM

@Alan -- looks like you can catch this error using the Application_Error event in Global.asax. Something like this:
void Application_Error(object sender, EventArgs e) 
{
Exception err = Server.GetLastError();
Session["lastError"] = err;
Server.Transfer("ErrorPage.aspx");
}


 
Fred   06 Jul 13 - 1:50 PM

Testing

 
Fred   06 Jul 13 - 1:51 PM

Test