Skip to main content

MS KB928365, ASP.NET Request.Headers.Add() Hack No Longer Works

So a project that I am currently a part of is using an ASP.NET 2.0 HttpModule to add some additional values to the incoming HTTP request's headers in the DEV environment (i.e., our local disconnected laptops) to simulate what an enterprise single-sign-on solution is performing in the production environment. It has worked like a charm. That is until I installed the new security update for the .NET Framework 2.0 release this past Wednesday, July 10, MS KB928365.

Apparently this "hack"
has been disabled with the release of this security update.

When attempting to call Headers.Add(), with or without the above hack in place, you will now receive a PlatformNotSupported exception.

All in all, this post is by no means a rant against the security update, but simply an attempt to add a quick answer to the "Google answer machine" for those searching. I am also already aware of a number of other potentially better solutions than the one currently in place for simulating those user accounts, but for now, simply uninstalling the above referenced security update gets the team back working again.

Comments

Anonymous said…
I go have the same problem.

You said you have better ideas. Can you share those...

I have SSO type app and I used to pass values using header.

Do you know any other way to add values to header to pass to other pagge....
Anonymous said…
Hi,

I managed to add a custom header to the Request.Headers collection by using the following code:

Imports System.Reflection

Public Class CustomHttpModule
Implements IHttpModule

Public Sub New()
' Class constructor.
End Sub


' Classes that inherit IHttpModule
' must implement the Init and Dispose methods.
Public Sub Init(ByVal app As HttpApplication) Implements IHttpModule.Init
AddHandler app.BeginRequest, AddressOf app_BeginRequest
End Sub


Public Sub Dispose() Implements IHttpModule.Dispose
' Add code to clean up the
' instance variables of a module.
End Sub

Public Sub app_BeginRequest(ByVal o As Object, ByVal ea As EventArgs)
Dim user As New ArrayList
Dim headers As NameValueCollection = HttpContext.Current.Request.Headers
Dim t As Type = headers.GetType()
t.InvokeMember("MakeReadWrite", System.Reflection.BindingFlags.InvokeMethod Or System.Reflection.BindingFlags.NonPublic Or System.Reflection.BindingFlags.Instance, Nothing, headers, Nothing)
t.InvokeMember("InvalidateCachedArrays", System.Reflection.BindingFlags.InvokeMethod Or System.Reflection.BindingFlags.NonPublic Or System.Reflection.BindingFlags.Instance, Nothing, headers, Nothing)
user.Add("user@domain.com")
t.InvokeMember("BaseAdd", System.Reflection.BindingFlags.InvokeMethod Or System.Reflection.BindingFlags.NonPublic Or System.Reflection.BindingFlags.Instance, Nothing, headers, New Object() {"SM_USER", user})
t.InvokeMember("MakeReadOnly", System.Reflection.BindingFlags.InvokeMethod Or System.Reflection.BindingFlags.NonPublic Or System.Reflection.BindingFlags.Instance, Nothing, headers, Nothing)
End Sub

End Class

Hope this helps.

Daniel M.
Anonymous said…
I've had a similar problem trying to simulate the passing of an SSO token to one of our web applications.

After banging my head against a brick wall for a coupple of hours I tried the above soloution with great success.
Thanks a million.

Im posting the c# version of the code below as it may help someone out in future :-)

HttpApplication objApp = (HttpApplication)r_objSender;
HttpRequest Request = (HttpContext)objApp.Context.Request;


//get a reference
NameValueCollection headers = Request.Headers;

//get a type
Type t = headers.GetType();
System.Collections.ArrayList item = new System.Collections.ArrayList();

t.InvokeMember("MakeReadWrite",BindingFlags.InvokeMethod | BindingFlags.NonPublic | BindingFlags.Instance,null,headers,null);
t.InvokeMember("InvalidateCachedArrays",BindingFlags.InvokeMethod | BindingFlags.NonPublic | BindingFlags.Instance,null,headers,null);
item.Add("CUSTOM_HEADER_VALUE");
t.InvokeMember("BaseAdd",BindingFlags.InvokeMethod | BindingFlags.NonPublic | BindingFlags.Instance,null,headers, new object[]{"CUSTOM_HEADER_NAME",item});
t.InvokeMember("MakeReadOnly",BindingFlags.InvokeMethod | BindingFlags.NonPublic | BindingFlags.Instance,null,headers,null);
Anonymous said…
FANTASTIC - THIS SOLVED MY PROBLEM!!!!

Thanks so much for this, I have been searching around the Google all day for a solution and have been unable to find anything that worked. I plugged in your C# Code which only needed a small modification:


HttpRequest Request = (HttpContext)objApp.Context.Request;

needed to be cast to:

HttpRequest Request = (HttpRequest)objApp.Context.Request;

AS I said I just plugged it in and VOILA!!! You turned my terrible and unproductive day into PROGRESS!!! thanks so much...

*Passes an internet Beer*
Anonymous said…
May gently someone post here a complete c# project? Thank you.
Jameel M said…
But where do you place this code?
Victor said…
I am trying to set the User-Agent header with a value if is not in the request. This code works great but I also need a way to set the Request.UserAgent property at the same time. I am not too hot on reflection so here is what I have been trying to do with no luck:

Dim u As Type = req.GetType()
u.InvokeMember("UserAgent", System.Reflection.BindingFlags.Default Or System.Reflection.BindingFlags.SetProperty Or System.Reflection.BindingFlags.NonPublic, Nothing, req, New Object() {"none"})

This fails with Method Not found. Oh and you can see that it is just converted to VB since the web app I am working in was written in VB.Net.
BigJimInDC said…
Victor,

If you're trying to do this and then pass the request off to another web server, then you should be looking into writing a proxy server, which is the correct way to deal with this.

On the other hand, if you know that you're not creating a proxy server, and you're just injecting this value into the headers for processing further down the line within your own code (within the same process even), then a better solution exists. Whether you use a DI/IOC container to do it, or you roll your own solution, you need to change your code so that the code that is trying to read this value from the HTTP headers is not directly relying on the values from the HTTP headers. One very easy solution is to simply create another class that sits in between the consuming code and the code that is reading from the headers. If you're running in a PRODuction environment, where the headers "are what they are", have the new class read from the value from the HTTP headers, and if you're running in a TEST environment, have the new class simply return the value that you're trying so hard to injection into the HTTP headers. Via DI/IOC, you would have two separate classes, one for each of those scenarios, that both implement the same interface, and then use one or the other depending on the environment. Simple as that!
Anonymous said…
Thanks a lot for the solution...
Anonymous said…
Thanks for the pattern to add a header value. I have used this example and see my Header value in the AllKeys collection. The problem I now have is that reading back the value throws an Invalid Cast Exception. Rather than recreate the entire explanation see my post at http://forums.asp.net/p/1638759/4236389.aspx#4236389

Thanks!
Nathan Rose said…
This worked really well for me as well. I was creating unit tests to test handling the HttpRequest headers via WCF data services. I created a method that added my custom headers to the request object and turned it into a utility for other future unit tests.

Method is as follows:

private void AddHeaderToRequest(HttpRequest request, string key, string value) {

NameValueCollection headers = request.Headers;

Type t = headers.GetType();
ArrayList item = new ArrayList();

// Remove read-only limitation on headers
t.InvokeMember("MakeReadWrite", BindingFlags.InvokeMethod | BindingFlags.NonPublic | BindingFlags.Instance, null, headers, null);
t.InvokeMember("InvalidateCachedArrays", BindingFlags.InvokeMethod | BindingFlags.NonPublic | BindingFlags.Instance, null, headers, null);
item.Add(value);
t.InvokeMember("BaseAdd", BindingFlags.InvokeMethod | BindingFlags.NonPublic | BindingFlags.Instance, null, headers, new object[] { key, item });
t.InvokeMember("MakeReadOnly", BindingFlags.InvokeMethod | BindingFlags.NonPublic | BindingFlags.Instance, null, headers, null);
}
Aaron Cardoz said…
Awesome post.

You can also extract that code into an Extension method

something like

public static void AddCustomerHeader(this HttpRequest httpRequest, string headerKey, string headerValue)

and then invoke it via HttpContext.Current.Request.AddCustomerHeader("Customer_Header_Key","Custom_Header_Value")

Popular posts from this blog

Temporal Database Design, Soft Deletes, and Ayende's Razor

This is a formal follow-up to a post on Ayende's blog on "Avoiding Soft Delete's" in your database where I question the lack of temporal database solutions being applied to these types of problems.

After Oren claimed the following, I felt it necessary to expand on it in a blog post of my own, rather than continuing to clutter his comments, and hopefully finally bring some traffic to my own blog :-)
Ayende’s RazorThis is a response to a comment on another post:Oren, in all seriousness, I thought that problems that were "(a) complex, (b) hard to understand (c) hard to optimize" were the kinds that folks like you and I get paid to solve...Given two solutions the match the requirements of the problem, the simpler one is the better.I could just as well call that statement "Jim's Razor", as I believe in it as much as you do Oren, so no arguments there.

But in the same vane, "wise" (i.e., experienced) software architects/developers strategical…

The Application of Pareto's Principle to the Adoption of Agile Practices - Part 1 of N

Starting this evening, I will be attending the Agile Coach Camp in Durham, NC. As the only registration fee for attending the ACC is to submit a position paper on a topic of interest to you, I submitted the following abstract.
The Application of Pareto's Principle to the Adoption of Agile Practices
If you believe in Pareto's Principle (otherwise known as the 80-20 Rule), then you believe that it can be applied literally everywhere. At its heart, Agile practices are about doing what works and ignoring the rest (at least until the time is right). In a world where people are constantly searching for silver bullets, getting distracted by zealot turf wars, and feeling the crunch of deadlines, novice adopters of Agile practices need to learn what out of"agile" is immediately important for their situation, and what they can safely ignore until a latter point in time.So I figure why not put some more concrete thoughts together before the ACC starts. This post is the …