Tuesday, 30 October 2012

The Joy of Forms Authentication, IIS7 and Device Profiles

We’re setting up a thing called PRTG Network Monitor to add monitoring and logging to one of our new MVC3 web applications, and I spent this morning scratching my head and wondering why an app that works just fine when you view it in a browser was consistently failing when I tried to attach an HTTP monitor to it.

A little digging with Wireshark later, and it turns out that IIS/ASP.NET isn’t sending the FormsAuthentication cookie in the response to requests made via the PRTG monitoring software. Which is… odd. We haven’t explicitly configured cookieless authentication or anything – although we’re doing some funky stuff with cross-domain auth cookies, the PRTG monitor is as simple as it gets – POST credentials to the login handler, get a response, check the response includes a cookie. Not rocket surgery.

After a fun hour of comparing TCP traces and HTTP headers, it turns out it’s the HTTP User Agent that’s controlling this behaviour – if ASP.NET running under IIS7 sees particular user agents, it just doesn’t set any cookies. And this is where it gets interesting. Here’s some real-world HTTP user agents that work just fine:

Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/21.0.1180.89 Safari/537.1

Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0);

Opera/9.80 (Windows NT 6.1; WOW64; U; en) Presto/2.10.289 Version/12.02

Here’s the PRTG monitoring software’s default user agent. Now this looks to me like a completely sensible user agent for a piece of monitoring software – but IIS7 clearly believes this to be a client that can’t handle HTTP cookies, and so won’t set any:

Mozilla/5.0 (compatible; PRTG Network Monitor (www.paessler.com); Windows)

But the really fun part – here’s a bunch of user agents that work absolutely fine:

Bozilla/5.0 (compatible; PRTG Network Monitor (www.paessler.com); Windows)

Batman/1.2 (compatible; spiderman; vigorous jazz-hands)


CookieHater1.2 (incompatible; does not accept cookies; no cookies; cookies are evil)

This doesn’t work:

Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML)

But these do:

Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like a BOSS)

Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like anyone cares)

In short - it looks like any string that starts with “Mozilla” triggers some sort of complicated parsing algorithm that looks for magic values in the user-agent string and decides that means they’re cookie-capable. I have this horrible, horrible feeling that somewhere in the bowels of the IIS team there’s somebody who still thinks browscap.ini was a good idea – and I’m amazed that something as fragile and idiosyncratic as determining device capabilities based on user agents would be the default behaviour in IIS7 and ASP.NET 4.

The good news is that this bizarre behaviour is easily fixed – just add cookieless="useCookies" to the system.web/authentication/forms element of your web.config file. The default is apparently “useDeviceProfile”, and there’s more details in the IIS documentation on Microsoft Technet.

This is also another classic example of how easily you can lose an entire day of developer time to something that should have taken about ten minutes. We have an app that uses HTTP and out-of-the-box cookie-based authentication. We’ve got something approaching 500 tests verifying the behaviour of this app under every scenario we could think of. We have a mature, stable monitoring platform that hundreds, if not thousands, of people are using to monitor HTTP applications every day. And yet BANG! you connect them together, everything falls apart and next thing you know you’re up to your elbows in network packets and wondering if it’s too early in the week to start drinking…

UPDATE: Thanks to @duncansmart for pointing me at this article on Scott Hanselman’s blog where he discusses this very problem.

No comments: