Archive for February, 2011

P3P header hell

February 25, 2011

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