This is a forked version of the redis-session-store gem. It incorporates a few changes:
- Configuring usage of a Redis connection pool.
- Passing the
nx: true
option when writing a new session to avoid session collisions. - Supporting the migration towards hashed session identifiers to more fully address GHSA-hrqr-hxpp-chr3.
- Removes calling
exists
in Redis to check whether a session exists and instead relying on the result ofget
.
gem 'redis-session-store', git: 'https://github.com/18F/redis-session-store.git', tag: 'v1.0.1-18f'
GHSA-hrqr-hxpp-chr3 describes a vulnerability to a timing attack when a key used by the backing store is the same key presented to the client. redis-session-store
(as of the most recent version 0.11.5) writes the same key to Redis as is presented to the client, typically in the cookie. To allow for a backwards and forwards-compatible zero-downtime migration from redis-session-store
and using Rack::Session::SessionId#private_id
to remediate the vulnerability, this forked version provides configuration options to read and write the two versions of the session identifier. A migration path would typically look like:
- Deploying with the following configuration, which is backwards-compatible:
Rails.application.config.session_store :redis_session_store,
# ...
redis: {
# ...
read_public_id: true,
write_public_id: true,
read_private_id: false,
write_private_id: false,
}
- Enabling writing to the private_id key
Rails.application.config.session_store :redis_session_store,
# ...
redis: {
# ...
read_public_id: true,
write_public_id: true,
read_private_id: false,
write_private_id: true,
}
- Enabling reading the private_id key and disabling reading the public_id key
Rails.application.config.session_store :redis_session_store,
# ...
redis: {
# ...
read_public_id: false,
write_public_id: true,
read_private_id: true,
write_private_id: true,
}
- Disabling writing for the public_id key
Rails.application.config.session_store :redis_session_store,
# ...
redis: {
# ...
read_public_id: false,
write_public_id: false,
read_private_id: true,
write_private_id: true,
}
See lib/redis-session-store.rb
for a list of valid options.
In your Rails app, throw in an initializer with the following contents:
Rails.application.config.session_store :redis_session_store,
key: 'your_session_key',
expire_after: nil, # cookie expiration
redis: {
ttl: 120.minutes, # Redis expiration
key_prefix: 'myapp:session:',
url: 'redis://localhost:6379/0',
}
If you want to handle cases where Redis is unavailable, a custom
callable handler may be provided as on_redis_down
:
Rails.application.config.session_store :redis_session_store,
# ... other options ...
on_redis_down: ->(e, env, sid) { do_something_will_ya!(e) }
redis: {
# ... redis options ...
}
By default the Marshal serializer is used. With Rails 4, you can use JSON as a custom serializer:
:marshal
- serialize cookie values withMarshal
(Default):json
- serialize cookie values withJSON
CustomClass
- You can just pass the constant name of a class that responds to.load
and.dump
Rails.application.config.session_store :redis_session_store,
# ... other options ...
serializer: :json
redis: {
# ... redis options ...
}
If you want to handle cases where the session data cannot be loaded, a
custom callable handler may be provided as on_session_load_error
which
will be given the error and the session ID.
Rails.application.config.session_store :redis_session_store,
# ... other options ...
on_session_load_error: ->(e, sid) { do_something_will_ya!(e) }
redis: {
# ... redis options ...
}
Note The session will always be destroyed when it cannot be loaded.
See CONTRIBUTING.md, AUTHORS.md, and LICENSE, respectively.