class Rack::Session::Cookie - rack-1.6.1 Documentation

# class Rack::Session::Cookie

Rack::Session::Cookie provides simple cookie based session management. By default, the session is a Ruby Hash stored as base64 encoded marshalled data set to :key (default: rack.session). The object that encodes the session data is configurable and must respond to `encode` and `decode`. Both methods must take a string and return a string.

When the secret key is set, cookie data is checked for data integrity. The old secret key is also accepted and allows graceful secret rotation.

Example:

    use Rack::Session::Cookie, :key => 'rack.session',
                               :domain => 'foo.com',
                               :path => '/',
                               :expire_after => 2592000,
                               :secret => 'change_me',
                               :old_secret => 'also_change_me'

    All parameters are optional.

Example of a cookie with no encoding:

    Rack::Session::Cookie.new(application, {
      :coder => Rack::Session::Cookie::Identity.new
    })

Example of a cookie with custom encoding:

    Rack::Session::Cookie.new(application, {
      :coder => Class.new {
        def encode(str); str.reverse; end
        def decode(str); str.reverse; end
      }.new
    })

### Attributes

coder[R]

### Public Class Methods

new(app, options={}) click to toggle source
Calls superclass method Rack::Session::Abstract::ID.new

    # File lib/rack/session/cookie.rb, line 105
          def initialize(app, options={})
            @secrets = options.values_at(:secret, :old_secret).compact
            warn "        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]}.
    " unless @secrets.size >= 1
            @coder  = options[:coder] ||= Base64::Marshal.new
            super(app, options.merge!(:cookie_only => true))
          end

### Private Instance Methods

destroy_session(env, session_id, options) click to toggle source

    # File lib/rack/session/cookie.rb, line 170
    def destroy_session(env, session_id, options)
      # Nothing to do here, data is in the client
      generate_sid unless options[:drop]
    end

digest_match?(data, digest) click to toggle source

    # File lib/rack/session/cookie.rb, line 175
    def digest_match?(data, digest)
      return unless data && digest
      @secrets.any? do |secret|
        Rack::Utils.secure_compare(digest, generate_hmac(data, secret))
      end
    end

extract_session_id(env) click to toggle source

    # File lib/rack/session/cookie.rb, line 128
    def extract_session_id(env)
      unpacked_cookie_data(env)["session_id"]
    end

generate_hmac(data, secret) click to toggle source

    # File lib/rack/session/cookie.rb, line 182
    def generate_hmac(data, secret)
      OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA1.new, secret, data)
    end

get_session(env, sid) click to toggle source

    # File lib/rack/session/cookie.rb, line 122
    def get_session(env, sid)
      data = unpacked_cookie_data(env)
      data = persistent_session_id!(data)
      [data["session_id"], data]
    end

persistent_session_id!(data, sid=nil) click to toggle source

    # File lib/rack/session/cookie.rb, line 148
    def persistent_session_id!(data, sid=nil)
      data ||= {}
      data["session_id"] ||= sid || generate_sid
      data
    end

set_session(env, session_id, session, options) click to toggle source

    # File lib/rack/session/cookie.rb, line 154
    def set_session(env, 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)
        env["rack.errors"].puts("Warning! Rack::Session::Cookie data size exceeds 4K.")
        nil
      else
        session_data
      end
    end

unpacked_cookie_data(env) click to toggle source

    # File lib/rack/session/cookie.rb, line 132
    def unpacked_cookie_data(env)
      env["rack.session.unpacked_cookie_data"] ||= begin
        request = Rack::Request.new(env)
        session_data = request.cookies[@key]

        if @secrets.size > 0 && session_data
          digest, session_data = session_data.reverse.split("--", 2)
          digest.reverse! if digest
          session_data.reverse! if session_data
          session_data = nil unless digest_match?(session_data, digest)
        end

        coder.decode(session_data) || {}
      end
    end