diff --git a/actionpack/lib/action_controller/cgi_process.rb b/actionpack/lib/action_controller/cgi_process.rb index 098ab860b874f..efbe558fbd4d2 100644 --- a/actionpack/lib/action_controller/cgi_process.rb +++ b/actionpack/lib/action_controller/cgi_process.rb @@ -13,8 +13,8 @@ class Base # (default). Additionally, there is CGI::Session::DRbStore and CGI::Session::ActiveRecordStore. Read more about these in # lib/action_controller/session. # * :session_key - the parameter name used for the session id. Defaults to '_session_id'. - # * :session_id - the session id to use. If not provided, then it is retrieved from the +session_key+ parameter - # of the request, or automatically generated for a new session. + # * :session_id - the session id to use. If not provided, then it is retrieved from the +session_key+ cookie, or + # automatically generated for a new session. # * :new_session - if true, force creation of a new session. If not set, a new session is only created if none currently # exists. If false, a new session is never created, and if none currently exists and the +session_id+ option is not set, # an ArgumentError is raised. @@ -24,6 +24,8 @@ class Base # server. # * :session_secure - if +true+, this session will only work over HTTPS. # * :session_path - the path for which this session applies. Defaults to the directory of the CGI script. + # * :cookie_only - if +true+ (the default), session IDs will only be accepted from cookies and not from + # the query string or POST parameters. This protects against session fixation attacks. def self.process_cgi(cgi = CGI.new, session_options = {}) new.process_cgi(cgi, session_options) end @@ -34,18 +36,21 @@ def process_cgi(cgi, session_options = {}) #:nodoc: end class CgiRequest < AbstractRequest #:nodoc: - attr_accessor :cgi, :session_options + 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 => "/" + :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 @@ -109,6 +114,9 @@ def session @session = Hash.new else stale_session_check! do + if @cookie_only && request_parameters[session_options_with_string_keys['session_key']] + raise SessionFixationAttempt + end case value = session_options_with_string_keys['new_session'] when true @session = new_session