In cgi_process, the CgiRequest constructor accepts a session_options hash as a parameter. It then deletes the :cookie_only attribute from the hash, and stores the value in an instance variable @cookie_only:
from actionpack-1.13.5/lib/action_controller/cgi_process.rb, line 38
class CgiRequest < AbstractRequest #:nodoc:
attr_accessor :cgi, :session_options, :cookie_only
class SessionFixationAttempt < StandardError; end #:nodoc:
DEFAULT_SESSION_OPTIONS = {
:database_manager => CGI::Session::PStore,
:prefix => "ruby_sess.",
:session_path => "/",
:cookie_only => true
} unless const_defined?(:DEFAULT_SESSION_OPTIONS)
def initialize(cgi, session_options = {})
@cgi = cgi
@session_options = session_options
@env = @cgi.send(:env_table)
@cookie_only = session_options.delete :cookie_only
super()
end
However, the implementation in the rails Dispatcher passes in the constant DEFAULT_SESSION_OPTIONS. So, the CgiRequest constructor is actually removing the :cookie_only attribute from DEFAULT_SESSION_OPTIONS. So, @cookie_only is set correctly the first time CgiRequest is instantiated, but every subsequent time, it's set to nil, which evaluates to false. So, the session fixation logic stops working after the first request:
from actionpack-1.13.5/lib/action_controller/cgi_process.rb, line 111
def session
<snip snip>
stale_session_check! do
if @cookie_only && request_parameters[session_options_with_string_keys['session_key']]
raise SessionFixationAttempt
end
<snip snip>
The CgiRequest constructor shouldn't delete the :cookie_only attribute from the session_options hash, and it doesn't need to have a member variable @cookie_only.
Instead, it could just look up the :cookie_only value from session_options_with_string_keys, which merges the DEFAULT_SESSION_OPTIONS and the passed in session_options (see attached diff).
With the proposed change, you'll be able to override the :cookie_only session option on a per action basis, like so:
class SomeController < ApplicationController
session :cookie_only => false, :only => :do_something
def do_something
render :text => "something"
end
end
Included in the attached diff is:
* my proposed patch
* updated unit tests that both highlight the problem and demonstrate the patch works