Why do some 'potentially dangerous Request.Path' HttpExceptions ignore httpErrors settings?
Asked Answered
P

2

0

I have a .Net website with custom error pages configured using the web.config httpErrors section:

<httpErrors errorMode="Custom" existingResponse="Replace">
  <clear/>
  <error statusCode="400" path="/page-not-found/" responseMode="ExecuteURL" />
  <error statusCode="404" path="/page-not-found/" responseMode="ExecuteURL" />
  <error statusCode="500" path="/error/" responseMode="ExecuteURL" />
</httpErrors>

and

<httpRuntime requestValidationMode="2.0" targetFramework="4.5" />

When I visit http://www.example.com/< the site displays the correct "page not found" page, however if I visit http://www.example.com/<a it displays a YSOD with a 500 status code response header.

I have Elmah hooked up and both URLs predictably throw exactly the same error System.Web.HttpException: A potentially dangerous Request.Path value was detected from the client (<).

But why are the two URLs not handled the same? Why is one rewritten to my custom error page, and the other not? I would expect the same behaviour from any exception.

Edit:

I should have mentioned up front that this was an Umbraco project. I thought that this wasn't a contributing factor but I created a barebones .Net project with no dependencies and this worked as expected, i.e. both URLs obeyed the httpErrors configuration. So this must be something Umbraco specific, probably a module.

Pal answered 10/2, 2015 at 10:39 Comment(0)
P
0

After a fair amount of digging, I can see that this was actually caused by a 3rd party module called ClientDependency. Removal of this and the normal behaviour returned but obviously in this case the backoffice will not function properly.

A patch on the project is required and I've submitted this.

Update:

This bug in ClientDependency has been fixed as of v1.8.3

Pal answered 15/2, 2015 at 11:56 Comment(0)
S
0

The validate helper method first calls validation for input and then paths.

The default values that are set in the default configuration for the validate helper method when validating input from the default web.config setting located at "syste.web/httRuntime" requestPathInvalidCharactersArray attribute are:

<,>,*,%,&,:,\,?

source: https://msdn.microsoft.com/en-us/library/system.web.configuration.httpruntimesection.requestpathinvalidcharacters(v=vs.110).aspx

As you can see "<" is being treated as an invalid PATH whereas "<a" would be treated as invalid INPUT and that is why you get two behaviors.

If you're curious as to how I figured this out, I just looked at the source code for the validate helper method and followed it back to the source.

[EDIT]

The ValidateInputIfRequiredByConfig Method shows it throwing a 400

[EDIT 2]

   public NameValueCollection QueryString
    {
        get
        {
            this.EnsureQueryString();
            if (this._flags[1])
            {
                this._flags.Clear(1);
                this.ValidateHttpValueCollection(this._queryString, RequestValidationSource.QueryString);
            }
            return this._queryString;
        }
    }

Calls to this property hit the ValidateHttpValueCollection can validate the querystring if the validation source is Querystring, which it is in this property.

In this method there is a method called ValidateString. In ValidateString it determines if it's a valid string or not and if not it throws a HttpRequestValidationException

Spondee answered 14/2, 2015 at 5:33 Comment(6)
I don't believe that is correct. Looking at the stack trace and referencing it against the .Net source it's clear that the exception is being thrown at the same point in ValidateInputIfRequiredByConfig(). What I need to know is why would one throw a 500 status code error instead of the expected 400?Pal
One is an invalid path (in the url) and produces a 400 because it cannot be found, right? The other is a 500 because it's invalid input. The codes thrown in the source reflect this as well.Spondee
No, that's just not correct. A 400 is a bad request not "not found" and nothing in the method throws a 500 exception, only 400. Finally, line 2612 would catch both paths and would therefore throw on line 2615. So what is throwing the 500?Pal
I updated the answer with another potential reason. See Edit 2.Spondee
I appreciate your time on this but it's not the QueryString but the Path property that it's failing on. You're right it is throwing a HttpRequestValidationException via the IsDangerousString() method which would account for the 500 status. However, the question still stands. Why is this ignoring the httpErrors configuration?Pal
I will look it the source some more but it's probably that the error is executed very early in the pipe and never gets to the logic that redirects you to your page in the config.Spondee
P
0

After a fair amount of digging, I can see that this was actually caused by a 3rd party module called ClientDependency. Removal of this and the normal behaviour returned but obviously in this case the backoffice will not function properly.

A patch on the project is required and I've submitted this.

Update:

This bug in ClientDependency has been fixed as of v1.8.3

Pal answered 15/2, 2015 at 11:56 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.