My day job finds me building infrastructural software, which supports all of our organization’s cloud products. It provides a suite of tenancy services including things like identity, throttling, and authentication. Capabilities such as these often entail a rather intimate relationship with an HTTP request and its lifecycle. The components of requests communicate a client’s intent and subtle variances can mean the difference between expected behavior and outright failure.
Implementation of these kinds of capabilities can be challenging with ASP.NET Core because of the way in which it manipulates and massages the components of an HTTP request. ASP.NET Core’s behavior appears to be tailored to support ASP.NET Core MVC’s routing system. It’s formal HttpRequest class exposes the components of a request but they rarely exactly reflect the specifications of the original request URL.
The HttpRequest class provides access to things like a request’s headers, it’s path, and query string. In doing so, it splits, parses, and decodes many of the constituent parts. The Headers dictionary is missing entries such as “Host”, presumably because it’s exposed a first-class property. The path has been bisected and exposed as two separate properties, Path and the vaguely documented PathBase, according to routing rules. You will also find that some of its values have been decoded for you while other remain in an encoded state.
The platform is clearly optimized for interpretation. It suits an application that needs only to infer intent. It effectively describes a request, however, under no circumstances can it be trusted to deliver the original request.
I was implementing a signature-based authentication procedure recently, when I stumbled upon a particularly nefarious bit of manipulation. In this procedure, the components of a request serve as input to the calculation of a signature. The client and server should both be able to assess the components of a request and calculate identical signatures. This assumes, of course, that the client and server both interpret a request in the same manner. The server needs access to the components of a request in their original form. (I’ve written previously about just how sensitive these techniques can be.)
Unfortunately, the HttpRequest delivers much of its data in a decoded form. What’s worse is that some of the values are just partially decoded. In the case of Path, some encoded characters are decoded while others remain encoded.
For my purposes, this wouldn’t suffice. What I really needed was an untainted, original, request target.
A quick Google search reveals a myriad of developers attempting to find the original URL for their requests and there are extensions methods available, which seek to reconstitute that URL. However, even these methods simply splice the manipulated parts back together.
Thankfully, some hunting through the Microsoft.AspNetCore.Http repository provided a solution. The IHttpRequestFeature interface exposes a RawTarget that is not surfaced on the HttpRequest class. (In fact, it’s the only property of IHttpRequestFeature that is not surfaced.) This property’s value reflects the original target, as specified in the client’s request. It has not been parsed or otherwise manipulated.
A simple extension method (shown below) makes the target readily accessible to any of your request processing logic.
|/// Gets the raw target of an HTTP request.|
|/// <returns>Raw target of an HTTP request</returns>|
|/// ASP.NET Core manipulates the HTTP request parameters exposed to pipeline|
|/// components via the HttpRequest class. This extension method delivers an untainted|
|/// request target. https://tools.ietf.org/html/rfc7230#section-5.3|
|public static string GetRawTarget(this HttpRequest request)|
|var httpRequestFeature = request.HttpContext.Features.Get<IHttpRequestFeature>();|
Photo by Hans Splinter / CC BY ND-2.0