All in the <head>

– Ponderings & code by Drew McLellan –

– Live from The Internets since 2003 –

About

How A Missing Favicon Broke My App for Chrome Users

14 February 2012

Excuse me while I let off some steam. I’ve just spent many hours debugging an authentication issue that was preventing Google Chrome users from logging into a PHP web app we’re currently working on. Here are the gory details for your amusement.

The app uses OAuth to authenticate against the client’s central auth service. That means instead of logging in to our app directly, the process is more like signing into Twitter from a third party – we send the user away to log in, they authenticate on the central server as normal and get sent back to us with a token which we can then use to log them in to our app.

The issue we were seeing is that Chrome users were clicking the login link, being sent out to authenticate, coming back and still being told they weren’t logged in. In all other browsers, returning from a successful login allowed access the app as expected. Puzzling.

As I went through debugging, it became clear that the point at which the process was failing was when the CSRF tokens were being compared. When we create the log in link, we add a CSRF token to the URL and at the same time store the same token in the user’s PHP session. On returning from authenticating, the token is handed back to us and we can compare it to the copy in the session storage to make sure there’s no funny business going on.

Poking further, it became clear that the reason the CSRF tokens didn’t match was due to the user being given a new PHP session when they returned. So when we compared the token, there was no stored value to compare it to. Very odd indeed.

Usually, if sessions go awry, the best thing to do is turn to the raw HTTP headers. A PHP session is of course based on a cookie – the server issues a session ID in a cookie and the browser presents it back to the server with each request. If the session ID was changing, that had to mean that either the browser wasn’t sending the cookie, or the server was issuing a new one. Either way, that would show up in the request and response headers.

Using the Network tab of the Chrome web inspector, I could see the headers for each file. I could see the cookie being set with the page response, and then presented back with the stylesheet request. Clicking out to log in and coming back to my page, I could see the cookie being presented again… but wait! With a different session ID. When did that change?

Double checking the headers again, I could see that no new cookie was being sent in the response to any of the items in the Network inspector. But there are requests that happen that the network inspector doesn’t show you. Like requesting a favicon.

As this app is in development, it doesn’t have a favicon, so the request should result in a 404. Except in this case, it didn’t. The server is configured (and to be fair, misconfigured) to route any requests that are not for real files or directories to my index.php page. This is so the app can have /any/url/it/pleases/ and it just gets routed through to my app to parse internally. A front controller.

This meant that instead of getting a 404 from Apache, the favicon request was being handed back the app’s home page. Niiice. But that shouldn’t be an issue, as even a request for a favicon is sent along with the cookie information, so PHP should pick up the session and carry on as normal. But the plot thickens. Apache is sitting behind Varnish, which I’d configured to strip cookies from any request for static files such as images, CSS, JavaScript, and, you know, favicons.

That meant that by the time the request got passed through to Apache, the cookie had been stripped and the request looked like a new user. Who was then given a brand new PHP session. As soon as I put a favicon in place, bingo, Chrome users could log in.

I’m not sure if Chrome requests the favicon at the beginning of the request, or right at the end, but either way, it was enough to change the user’s session cookie between the end of one page load and the beginning of the next.

A perfect storm of a bit of misconfiguration on my part, a lack of transparency from Chrome’s network inspector, and a missing favicon.

- Drew McLellan

Comments

  1. § Dirk Olbertz:

    Yeah, it had such a problem a couple of years ago, but with all web browsers. That was also due to an CSRF token in the session and a missing favicon.

    And every since then I shrug when I hear about browsers who want to prefetch any URL it finds on a website. Until now, I didn’t run into another problem with such a feature, but I’m sure it won’ take long…

  2. § Mattias:

    In all fairness, that’s not really a Chrome issue, but as you mentioned a misconfigured 404 page where the cookies were being stripped.
    While it’s annoying indeed that Chrome would use the first request (presumably favicon.ico) to re-use that PHPSESSID cookie, it may seem like normal RFC-like behavior for the browser.

  3. § Drew McLellan:

    Agreed, it’s not a Chrome issue – I hope I framed it correctly in saying that my app was broken for Chrome users. Not that Chrome was at fault.

    The only thing that Chrome could have done better is fully report all the network activity in the Network inspector.

  4. § David Collantes:

    The title should read “How an improperly configured Apache broke my app for Chrome users”

  5. § James Wragg:

    I often find myself turning to Charles to see what’s really going on with network traffic. It’s invaluable for understanding what’s going on when working with HTML5 appcache for instance & I think could have saved you some frustration here.

  6. § Drew McLellan:

    David - That would also be accurate, but the missing favicon is actually an important piece of the puzzle, as it’s unusual in that it’s a ‘stealth’ request not reported in the dev tools.

    Were the issue something like a missing stylesheet, that would have been far quicker to debug, as I would have see the incorrect response in the inspector.

  7. § Drew McLellan:

    James – Yes, Charles is great. A more blunt tool, but the Firefox Live HTTP Headers plugin is also a useful one, and requires no configuration. Had this issue not been Chrome-specific, I would have probably turned to that.

  8. § Randy Burden:

    Just reading the title of this post I somehow knew the problem would end up being a new session created when the favicon was requested.

    @James, yes, I too would have whipped out a trusty HTTP proxy but my app of choice is Fiddler.

  9. § Abraham Williams:

    Looks like Chrome has an open bug report for favicon request not showing up on the network tab. http://code.google.com/p/chromium/issues/detail?id=110449

  10. § Jacob Williams:

    I had this problem once before. The application was a hybrid of .NET and ColdFusion behind a load balancer. Each part had its own pool of app servers and requests for the favicon within the .NET pool were getting redirected to the ColdFusion pool which was the default for the root of the domain. This caused all cookies for the load balancer’s stickiness to be reset on every request and a user would hop from server to server within each pool. Since logins were server specific, it would take 3-5 logins before the user was logged in on all of them. Took a while to track it down.

Photographs

Work With Me

edgeofmyseat.com logo

At edgeofmyseat.com we build custom content management systems, ecommerce solutions and develop web apps.

Follow me

Affiliation

  • Web Standards Project
  • Britpack
  • 24 ways

I made

Perch - a really little cms

About Drew McLellan

Photo of Drew McLellan

Drew McLellan (@drewm) has been hacking on the web since around 1996 following an unfortunate incident with a margarine tub. Since then he’s spread himself between both front- and back-end development projects, and now is Director and Senior Web Developer at edgeofmyseat.com in Maidenhead, UK (GEO: 51.5217, -0.7177). Prior to this, Drew was a Web Developer for Yahoo!, and before that primarily worked as a technical lead within design and branding agencies for clients such as Nissan, Goodyear Dunlop, Siemens/Bosch, Cadburys, ICI Dulux and Virgin.net. Somewhere along the way, Drew managed to get himself embroiled with Dreamweaver and was made an early Macromedia Evangelist for that product. This lead to book deals, public appearances, fame, glory, and his eventual downfall.

Picking himself up again, Drew is now a strong advocate for best practises, and stood as Group Lead for The Web Standards Project 2006-08. He has had articles published by A List Apart, Adobe, and O’Reilly Media’s XML.com, mostly due to mistaken identity. Drew is a proponent of the lower-case semantic web, and is currently expending energies in the direction of the microformats movement, with particular interests in making parsers an off-the-shelf commodity and developing simple UI conventions. He writes here at all in the head and, with a little help from his friends, at 24 ways.