How to handle "A potentially dangerous Request.Form value was detected from the client"?

Viewed 3044

I've received some "generic" errors within the protected void Application_Error() handler on an ASP.NET MVC Framework application. The exception message is:

A potentially dangerous Request.Form value was detected from the client

There are many ways to trigger this; just one example is making a call to e.g.,

http:/www.mywebsite.com/http:/www.mywebsite.com/

I'd like to create a "filter" for this kind of exception only, and redirect or manage the request accordingly. I don't want to disable it, just to manage it. I also don't want it to fall within the generic protected void Application_Error() handler—or, at least, I want to manage it internally within that method, so that it doesn't get handled by e.g. my logging.

How can I manage this?

1 Answers

It sounds like you already have an Application_Error() event handler configured, but don't want your standard logging to include this type of error, presumably because it's cluttering your logs with exceptions that you don’t intend to investigate further.

Background

This error is supposed to be represented by an HttpRequestValidationException. If this were the case here, of course, this would be a trivial problem. Based on the test case you've provided, however, you're actually getting the more general HttpException.

As you noted in the comments, this is due to the .NET Framework performing validation of characters in the URL early in the request pipeline, prior to the RequestValidator being evaluated. This may be a factor in why the HttpRequestValidationException isn't getting thrown, as would otherwise happen if the RequestValidator's IsValidRequestString() method returned false.

Workaround

Unfortunately, as the HttpException class is used for a wide range of errors, this makes it difficult to handle. Fortunately, as you also discovered, you can push this validation to the RequestValidator by disabling the requestPathInvalidCharacters in the web.config and configuring a custom RequestValidator:

<configuration>
  <system.web>
    <compilation targetFramework="4.6.1" />
    <httpRuntime 
      targetFramework="4.6.1" 
      requestValidationType="CustomRequestValidator" 
      requestPathInvalidCharacters="" 
    />
  </system.web>
</configuration>

Note: The requestValidationType will need to be prefixed with its namespace, assuming it's not in your project's root namespace.

The default value for the requestPathInvalidCharacters is <,>,*,%,&,:,\,?, so you'll now need to reproduce that validation in your CustomRequestValidator:

public class CustomRequestValidator : RequestValidator
{
    private static readonly char[] requestPathInvalidCharacters = new [] { '<', '>', '*', '%', '&', ':', '\\' };
    protected override bool IsValidRequestString(
        HttpContext context, 
        string value, 
        RequestValidationSource requestValidationSource, 
        string collectionKey, 
        out int validationFailureIndex
    ) {
        if (requestValidationSource is RequestValidationSource.PathInfo || requestValidationSource is RequestValidationSource.Path) {
            var errorIndex = value.IndexOfAny(requestPathInvalidCharacters);
            if (errorIndex >= 0) {
                validationFailureIndex = errorIndex;
                return false;
            }
        }
        return base.IsValidRequestString(
            context, 
            value, 
            requestValidationSource, 
            collectionKey, 
            out validationFailureIndex
        );
    }
}

This first recreates the validation of the path (represented by the value parameter), and then performs the standard out-of-the-box IsValidRequestString() processing from the base RequestValidator. This will now throw the expected HttpRequestValidationException.

Managing the Exception

At this point, as noted above, this now becomes a trivial problem. As the RequestValidator is evaluated prior to MVC routes being parsed, you still can't use a Global Exception Filter here. Since you already have an Application_Error() event handler setup, however, you can manage this in your global.asax.cs with something like:

void Application_Error(object sender, EventArgs e)
{

    Exception ex = Server.GetLastError();

    if (ex is HttpRequestValidationException)
    {
        Response.Clear();
        Response.StatusCode = 200;
        // Manage request as you deem appropriate; e.g.,
        Response.Redirect("~/Errors/RequestValidation/");
        Response.End();
        return;
    }

    // Your current logging logic

}

Additional Details

The RequestValidator gets called at least twice for nearly every ASP.NET request—once for the pathinfo (which is typically empty) and once for the path (see requestValidationSource). Every QueryString, Form, Cookie, Header, or file bound to the route will also be validated.

Microsoft tries to limit what requests necessitate validation. For instance, they don't check requests to static files. Further, parameters not bound to the route are skipped. Since potentially dangerous characters cannot be used in route parameter keys, we don't need to worry about validating those.

Further, as you noted, there's also no need to validate parameter values (e.g., a cookie value) for invalid path characters. Collection values will still be evaluated for the presence of other potentially dangerous strings—such as <script—by the base IsValidRequestString() logic.

Related