class Rack::Session::Cookie
def delete_session(req, session_id, options)
def delete_session(req, session_id, options) # Nothing to do here, data is in the client generate_sid unless options[:drop] end
def digest_match?(data, digest)
def digest_match?(data, digest) return unless data && digest @secrets.any? do |secret| Rack::Utils.secure_compare(digest, generate_hmac(data, secret)) end end
def extract_session_id(request)
def extract_session_id(request) unpacked_cookie_data(request)["session_id"] end
def find_session(req, sid)
def find_session(req, sid) data = unpacked_cookie_data(req) data = persistent_session_id!(data) [data["session_id"], data] end
def generate_hmac(data, secret)
def generate_hmac(data, secret) OpenSSL::HMAC.hexdigest(@hmac.new, secret, data) end
def initialize(app, options = {})
def initialize(app, options = {}) @secrets = options.values_at(:secret, :old_secret).compact @hmac = options.fetch(:hmac, OpenSSL::Digest::SHA1) warn <<-MSG unless secure?(options) SECURITY WARNING: No secret option provided to Rack::Session::Cookie. This poses a security threat. It is strongly recommended that you provide a secret to prevent exploits that may be possible from crafted cookies. This will not be supported in future versions of Rack, and future versions will even invalidate your existing user cookies. Called from: #{caller[0]}. MSG @coder = options[:coder] ||= Base64::Marshal.new super(app, options.merge!(cookie_only: true)) end
def persistent_session_id!(data, sid = nil)
def persistent_session_id!(data, sid = nil) data ||= {} data["session_id"] ||= sid || generate_sid data end
def secure?(options)
def secure?(options) @secrets.size >= 1 || (options[:coder] && options[:let_coder_handle_secure_encoding]) end
def unpacked_cookie_data(request)
def unpacked_cookie_data(request) request.fetch_header(RACK_SESSION_UNPACKED_COOKIE_DATA) do |k| session_data = request.cookies[@key] if @secrets.size > 0 && session_data session_data, _, digest = session_data.rpartition('--') session_data = nil unless digest_match?(session_data, digest) end request.set_header(k, coder.decode(session_data) || {}) end end
def write_session(req, session_id, session, options)
def write_session(req, session_id, session, options) session = session.merge("session_id" => session_id) session_data = coder.encode(session) if @secrets.first session_data << "--#{generate_hmac(session_data, @secrets.first)}" end if session_data.size > (4096 - @key.size) req.get_header(RACK_ERRORS).puts("Warning! Rack::Session::Cookie data size exceeds 4K.") nil else SessionId.new(session_id, session_data) end end