Jpmobileのtrans_sid(2)
Rails2.2上では
dispatch_cgiでCGIオブジェクトをつくるときに
actionpack/lib/action_controller/dispatcher.rb
def dispatch_cgi(cgi, session_options) if cgi ||= self.class.failsafe_response(@output, '400 Bad Request') { CGI.new } @request = CgiRequest.new(cgi, session_options) @response = CgiResponse.new(cgi) dispatch end rescue Exception => exception failsafe_rescue exception end
CGIのinitializeで呼ばれるCGI::QueryExtensionのinitialize_queryを上書きして
CGIオブジェクトのインスタンス変数@paramsを{}にしてしまう
actionpack/lib/action_controller/cgi_ext/query_extension.rb
require 'cgi' class CGI #:nodoc: module QueryExtension # Remove the old initialize_query method before redefining it. remove_method :initialize_query # Neuter CGI parameter parsing. def initialize_query # Fix some strange request environments. env_table['REQUEST_METHOD'] ||= 'GET' # POST assumes missing Content-Type is application/x-www-form-urlencoded. if env_table['CONTENT_TYPE'].blank? && env_table['REQUEST_METHOD'] == 'POST' env_table['CONTENT_TYPE'] = 'application/x-www-form-urlencoded' end @cookies = CGI::Cookie::parse(env_table['HTTP_COOKIE'] || env_table['COOKIE']) @params = {} end end end
このため、パラメータにセッションキーがあってもCgi::Sessionの初期化時にsession_idにするとしているところに
ひっかからない。
cgi/session.rb
def initialize(request, option={}) @new_session = false session_key = option['session_key'] || '_session_id' session_id = option['session_id'] unless session_id if option['new_session'] session_id = create_new_id end end unless session_id if request.key?(session_key) #ここで書き込んだパラメータがsession_idになる session_id = request[session_key] session_id = session_id.read if session_id.respond_to?(:read) end unless session_id session_id, = request.cookies[session_key] end unless session_id unless option.fetch('new_session', true) raise ArgumentError, "session_key `%s' should be supplied"%session_key end session_id = create_new_id end end @session_id = session_id
Jpmobileのtrans_sidでは、Cgi::Sessionの初期化の直前にCGIパラメータにセッションキーと値を書き込んで、
クエリストリング・POSTデータからsession_idが作られるようにしているわけですか・・
jpmobile/lib/jpmobile/trans_sid.rb
class << self def trans_sid(mode=:mobile) include Jpmobile::TransSid self.trans_sid_mode = mode unless mode == :none # CSRF対策への対策 session :cookie_only => false # url/postからsession_keyを取得できるようhookを追加する unless ::CGI::Session.private_method_defined?(:initialize_without_session_key_fixation) ::CGI::Session.class_eval do alias_method :initialize_without_session_key_fixation, :initialize def initialize(cgi, options = {}) key = options['session_key'] if cgi.cookies[key].empty? session_id = (CGI.parse(cgi.env_table['RAW_POST_DATA'])[key] rescue nil) || (CGI.parse(ENV['QUERY_STRING'] || cgi.query_string)[key] rescue nil) cgi.params[key] = session_id unless session_id.blank? #ここでパラメータに書き込み end initialize_without_session_key_fixation(cgi, options) end end end end end