Saturday, February 21, 2009

IE POST and redirect errors...

This blog post only exists to prevent expired linking. My blog has moved to: http://pabloaizpiri.com/

--------------------------------------------------------------------------------

UPDATE NOTE: I did find a much simple solution to this; as mentioned in the comments at the bottom,  I found that sending a compressed P3P security header did just fine to address the redirect issue and was much more simple to implement.
The HTTP module is now modified to send the P3P header on redirect. You can do so as follows:
_context.Response.AddHeader("p3p", "[P3P Policy Here]")
You can find more information about P3P here.

Many developers use redirection as a tool to present the user with another page once a process has been completed. One of the most common uses is- for example- a login. A user logs in, and- if he is authenticated he is redirected to another page, if not the same page is displayed with an error message informing the user why the authentication failed. (Such as an incorrect username/password combination)

I don't know if I would consider this the best way to carry out these types of transactions, since in my understanding that isn't exactly what redirects were meant for- however, it is a simple way to address these type of situations and on strictly pragmatic terms- can often be the best approach.

I've been working on a page that does exactly this, when I ran into an error.


The Problem.

Apparently, if you do a POST to a page and that page sends back headers to set a cookie AND redirect in the same response, the cookie does not get set. This seems to be a bug in IE. And the fact that the page was originally called from an iframe seemed to be key to the problem.

How did I run into this? I was working with another system that seamlessly integrated into the web application, and it happened to open the website in an iframe. The process was mostly simple:
  1. Client used Web Application A which makes a form post to Web Application B within an iframe. (Web Application A uses iframes)

  2. Web Application B processes the post for authentication.

  3. If the authentication succeeds, Web Application B redirects the client to a "services" pages also on Web Application B that offer services.

  4. The client is now looking at a "services" page on Web Application B which is within an iframe of the Web Application A.
The problem was between steps 3 and 4 - in that the 4th step would never happen. The client was certainly redirected to the "services" page but the "services" page would redirect the client right back to a login page because the client was not authenticated. In step 3 the server would attempt to send back a cookie to identify the session and redirect in the same response, but the cookie would never be set and never sent back.

It was an "intermittent" problem- and I still haven't gotten it to replicate more than twice to figure our the exact conditions under which it does work; the rest of the time it failed.

However, a suspicion tells me it was not an intermittent error, but rather that it always happens. (After all, there are no intermittent errors. It's all a matter of finding out the exact conditions under which these anomalies occur)
I noticed that when the browser did successfully set the cookie and return it, it was most likely because I had previously signed on the site before, effectively forcing the browser to send back my old authentication cookie- "seemingly" making it work.

Anyway, I found a blog by Brian Donahue where it seems like he ran into the same bug, or at least a related bug:
http://www.persistall.com/archive/2008/01/25/cookies--redirects--nightmares.aspx

It was the closest thing I could find to the problem I was having. After attempting the solutions provided with no success, it seems to me like there is a little more to it, and in this case, the only thing I can think different is the fact that in my case it involves an iframe. However, something tells me that this can't be it. For one, it doesn't make sense, but I'm also not completely ruling it out- there just may be something about the way cookies are handled within an iframe that I don't know about, but basically I'm still wondering why the solutions Brian Donahue wrote about did nothing for me. I would be more than appreciative to anyone who could educate me better on why this happens- for now I will continue to attempt to find the exact reason for this bug!


The Consequences.

Anyway, this cookie not being set was a rather large problem, because this would mean the user would not be authenticated. This would destroy the communication between both systems so I had to think of some way to fix the problem.

I started looking into the session object in ASP.NET and how that is handled, as well as just all the HTTP Pipeline in general.

I thought about enabling cookie-less sessions, and it might have worked out if I could use them on a per-page basis, but that was quite a port-over and not as secure. I hated the idea of an ID being displayed on the address bar, (especially for each pages) both for security and just aesthetic reasons, really. All in all, I finally decided against enabling cookie-less sessions.


Addressing The Problem.

How was this problem solved?

I wrote an HTTP Module. The module essentially "interrupts" the response, checks if the response is a redirect and sets a cookie- and if it does- it changes the URL direction by appending to the url a session ID, (like the cookie-less session feature) and a key that quickly expires.

The receiving page "interrupts" the request, checks for this session ID, generates a cookie with the ID value from the query string, and feeds this artificial cookie into the HTTP the pipeline. The session is then loaded normally by ASP.NET.
There is also some logic to check the key that was passed in for validity, as well to check if it was expired- if so, the session is destroyed effectively logging the user out before any of the code in the page object is executed.

I felt this solution was great for a few reasons:
  • The URL will virtually never display a session ID.
    For 98% for the application usage, the URL will never contain a session ID. When it does, it will be more secure on account of the expiring key. It will only show on those specific URLs that redirect and attempt setting a cookie at the same time.
    Currently the only page that redirect this way is linked from an iframe, which means the user will never see the page address; now I can lean towards 99.99%

  • More Secure than the enabling session-less cookies.
    The security is much tighter than cookie less sessions because an expiring key must also be authenticated when the session data is passed on.

  • It only affects redirects that set cookies.
    This was great in that I didn't want to add complexity to the website and overhead that was unnecessary.

  • It is completely transparent to other developers.
The last point was one of the greatest benefits. This means any other developer can come after me, and write his pages like he normally does, without ever even being aware of the IE Redirect bug. Because the code works on a lower level in the HTTP Pipeline chain, this is all done transparently.

This drives the cost of ownership of the application down because future developers will not have to be versed on the changes or develop specifically to accommodate for the change, or even run into the error in the first place and attempt to address it. This drives development time down which translates to more time in other projects and no money wasted.

Until next time,
- Jheatt