Recently my team was asked to implement an HttpModule for an ASP.NET MVC application that handled double-encoded URLs on IIS 7 and .NET 3.5. Here's the crux of the problem:
We sometimes get URLs that have double-encoded forward slashes that look like so:
http://www.example.com/%252fbar%5cbaz/foo
There are other formats that we have to handle as well, but they all have something in common, they have a double-encoded forward slash.
To fix this, we wrote an HttpModule that only acts when a URL has a double encoded forward slash, and we redirect it to a sane URL. The details aren't important, but there are two bits that are:
Here's the problem:
The first request after IIS starts up shows a different URL than the second request does.
If we used the URL from the above example, the first request to IIS would look like:
http://www.example.com/bar/baz/foo
and the second request would look like:
http://www.example.com/%252fbar%5cbaz/foo
This was done by inspecting the Application.Request.Url.AbsolutePath
property while debugging.
Here's the smallest code example that should reproduce the problem (create a new MVC application, and register the following HttpModule):
public class ForwardSlashHttpModule : IHttpModule
{
internal IHttpApplication Application { get; set; }
public void Dispose()
{
Application = null;
}
public void Init(HttpApplication context)
{
Initialize(new HttpApplicationAdapter(context));
}
internal void Initialize(IHttpApplication context)
{
Application = context;
context.BeginRequest += context_BeginRequest;
}
internal void context_BeginRequest(object sender, EventArgs e)
{
var url = Application.Request.Url.AbsolutePath; //<-- Problem point
//Do stuff with Url here.
}
}
Then, call the same URL on localhost:
http://www.example.com/%252fbar%5c/foo
NB: Make sure to insert a
Debugger.Launch()
call before the line incontext_BeginRequest
so that you'll be able to see it the first time IIS launches
When you execute the first request, you should see:
http://example.com/bar/foo
on subsequent requests, you should see:
http://example.com//bar/foo
.
My question is: Is this a bug in IIS? Why does it provide different URLs when calling Application.Request.Url.AbsolutePath
the first time, but not for any subsequent request?
Also: It doesn't matter whether the first request is for a double encoded URL or not, the second request will always be handled appropriately by IIS (or at least, as appropriate as handling double-encoded forward slashes can be). It's that very first request that is the problem.
I tried a few different properties to see if one had different values on the first request:
First Requeststring u = Application.Request.Url.AbsoluteUri;
"http://example.com/foo/baz/bar/"
string x = Application.Request.Url.OriginalString;
"http://example.com:80/foo/baz/bar"
string y = Application.Request.RawUrl;
"/%2ffo/baz/bar"
bool z = Application.Request.Url.IsWellFormedOriginalString();
true
The only interesting thing is that the Application.Request.RawUrl
emits a single-encoded Forward slash (%2f
), and translates the encoded backslash (%5c
) to a forwardslash (although everything else does that as well).
The RawUrl
is still partially encoded on the first request.
string u = Application.Request.Url.AbsoluteUri;
"http://example.com//foo/baz/bar"
string x = Application.Request.Url.OriginalString;
"http://example.com:80/%2ffoo/baz/bar"
string y = Application.Request.RawUrl;
"/%2ffoo/baz/bar"
bool z = Application.Request.Url.IsWellFormedOriginalString();
false
Interesting points from the second request:
IsWellFormedOriginalString()
is false
. On the first request it was true
.AbsoluteUri
is different. On the second request, it has two forward slashes.Application.Request.ServerVariables["URL"] = /quotes/gc/v12/CMX
Application.Request.ServerVariables["CACHE_URL"] = http://example.com:80/%2ffoo/baz/bar
Open Questions
iisreset
Keep in mind, the physical impact of this problem is low: For it to be an actual problem, the first request to the web server from a client would have to be for the above specific URL, and the chances of that happening are relatively low.
Request.Url can be decoded already - I wouldn't trust it for what you are doing.
See the internal details at: Querystring with url-encoded ampersand prematurely decoded within Request.Url
The solution is to access the values directly via Request.RawUrl.
I realize your prob is with the path, but it seems the same thing is going on. Try the RawUrl - see if it works for you instead.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With