P3P header hell

If you’ve ever embedded a Rails app in an iframe, then you have probably come across a big problem: IE will refuse cookies from the embedded site, so your session info gets trashed.

There’s lots of info on the web about this “feature” of IE, and the solution involves setting a P3P header to return details of your “privacy policy”. Chapter and verse here

The simple Rails equivalent is this:

class ApplicationController < ActionController::Base
  
  before_filter :set_p3p
  
  private
  
  # this is required by IE so that we can set session cookies
  def set_p3p
    headers['P3P'] = 'CP="ALL DSP COR CURa ADMa DEVa OUR IND COM NAV"'
  end
end

At first sight this seems to work. But don’t get too excited: after a few more page refreshes, it suddenly stops working, IE trashes the session, and your user is mysteriously logged out of your app.

The reason is described, along with a solution, here. In short, Rails’ etagging functionality, which is enabled by default (at least, it is in 2.3.8) means that sooner or later IE is going to request a page which Rails will know has not changed in the interim. When this happens Rails returns a 304 Not Changed header, which crucially doesn’t include your oh-so-important P3P header. Net result – IE trashes the session.

You can mangle Rails to set the P3P header regardless, but as the W3C prohibits these headers in this type of response, some webservers, including Apache, will strip them out again.

The very helpful solution described above will work, but at the expense of disabling etags for the entire app. This negates the benefit of etags and will result in slower navigation for your users, and increased load on your server. So why should everyone suffer because of Microsoft’s much-loathed browser?

The solution is to disable etags only if the browser is IE, leaving it switched on for everyone else. Stick this in your /lib directory and require it from your environment.rb:

# The purpose of this is to disable etags when processing requests from Internet Explorer
# This is because IE requires a P3P header in order to accept cookies within an iframe
# and if etags are used then subsequent calls for the same page are met with a 304 not changed
# response, which is not allowed (by W3C) to contain P3P headers.  Apache strips them even if we
# patch Rails to set them anyway

# see refs:
# Apache filters P3P headers from 304 responses: http://groups.google.com/group/rack-devel/browse_thread/thread/11da5971522b107b
# general explanation of the problem: http://tempe.st/tag/ruby-on-rails/

module ActionController
  
  class Request
    
    alias_method :etag_matches_original?, :etag_matches?
    
    def etag_matches?(etag)
      !env['HTTP_USER_AGENT'].include?('MSIE') && etag_matches_original?(etag)
    end
  
  end
 
  class Response
    
    alias_method :etag_original?, :etag?
    
    def etag?
      request.env['HTTP_USER_AGENT'].include?('MSIE') || etag_original?
    end
    
    
  end
end
Advertisements

Tags:

One Response to “P3P header hell”

  1. pragmatig Says:

    Just fixed this in a gem/plugin, hope it helps you to escape this hell 🙂

    https://github.com/grosser/ie_iframe_cookies

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s


%d bloggers like this: