Thursday, 8 January 2004
In ASP.NET 1.1, they added a validation check that scans posted information and throws an error if "potentially dangerous" information is detected. This check is on by default; you can disable it by setting the ValidateRequest attribute of the @Page directive to false or, for a site-wide setting, doing the same in Web.config. The check is applied to anything in the Request.Forms collection, to the Request.QueryString collection, and to the Request.Cookies collection.
The point is to help guard against script injection exploits. If this is somehow new to you, you can find information on it here, here, and here.
People occasionally ask what the validation check looks for exactly. When they initially added the check, it was not documented for whatever reason. However, it's useful for people to understand what the check does -- and doesn't -- do. The security guys in ASP.NET note that the ValidateRequest check is not so secure that you should rely on it exclsively anyway; it was designed to provide a first level of protection for people who otherwise were not too aware of the dangers of script exploits, which is also why it's on by default.
If you ask someone on the ASP.NET team what the validation check does, they'll tell you that it's "the equivalent of" the following regular expression:
This is slightly misleading, because the check is not implemented as a regular expression. Instead, the check is coded manually, so to speak, primarily for performance reasons -- parsing, compiling, and executing a regular expression for each postback would add significant overhead to postback processing. Nonetheless, the statement is true in that the validation check does perform a check very close to that represented by the preceding regular expression.
If you're fluent in regular expressions, the above expression should be a good guide to the check. If regular expressions are not your thing, however, you might find the following analysis of the check a little more helpful. Note that although the "equivalent to" regular expression above shows lowercase letters ([a-z]), the real check is case insensitive. The validation checks looks for and throws an error on these strings:
The check makes an effort to narrow the range of forbidden strings down so that, for example, it will allow the character < through as long as it's not the beginning of a tag. Even so, however, many people find the check a little too heavy handed because it throws on content that is usually benign, like <b> or —.
- The character "<" followed by any alpha character. This excludes most HTML tags.
- The character sequence "<!", which excludes HTML comments.
- The character sequence "&#", which guards against injection of an encoded character like <.
- The string "on" preceded by white space, possibly followed by alpha characters, possibly followed by whitespace, followed by "=". This guards against things like "onclick=", while still allowing the string "on" inside of another string, such as "bonny lass." Note that if you have code that says something like Boolean on = true;, you're out of luck.
- The word "expression" followed by an open parenthesis. This guards against a string like style="aaa:expression(alert('Attack!'))"
This gets back to the philosophy of the validation check, which is to try to protect people who don't otherwise know how to protect themselves from script injection. Moreover, the check follows a security philosophy that it's better to allow too little data through than too much. The ASP.NET team's notion is that if their default check is not to your liking, then you should turn off ValidateRequest for the page or in web.config and implement your own check and/or filter. Doing so is left as an exercise for the coder. If the information you get from a user would ever be displayed in a Web page, you'd want to apply Server.HtmlEncode and Server.UrlEncode to it before displaying it, or perhaps before storing it in a database or whatever. Creating a filter that allows some tags but not others can get tricky for anything beyond simple tags like <b> that have no attributes. For example, creating a filter to check for non-malicious <a> tags would be a, you know, fun exercise.
One final note. You cannot directly catch the exception thrown by ValidationRequest in the page. That is, you can't code a try-catch block in the page to work with the exception. However, you can use standard error-handling techniques to redirect on an error. For example, you can code a Page_Error handler and in it use Server.Transfer to redirect to an error-handling page, where you can get the most recent exception from Server.GetLastError().