Skip to content

Commit 0db1d35

Browse files
tmandkestevenharman
authored andcommitted
Make fetching insecure session fallback configurable
1 parent 734c38f commit 0db1d35

File tree

3 files changed

+36
-1
lines changed

3 files changed

+36
-1
lines changed

README.md

+6
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,12 @@ been updated in the last 30 days. The 30 days cutoff can be changed using the
3838
Configuration
3939
--------------
4040

41+
Disable fallback to use insecure session by providing the option `secure_session_only`
42+
when setting up the session store.
43+
```ruby
44+
Rails.application.config.session_store :active_record_store, :key => '_my_app_session', :secure_session_only => true
45+
```
46+
4147
The default assumes a `sessions` table with columns:
4248

4349
* `id` (numeric primary key),

lib/action_dispatch/session/active_record_store.rb

+7-1
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,12 @@ class ActiveRecordStore < ActionDispatch::Session::AbstractSecureStore
6060
SESSION_RECORD_KEY = 'rack.session.record'
6161
ENV_SESSION_OPTIONS_KEY = Rack::RACK_SESSION_OPTIONS
6262

63+
def initialize(app, options = {})
64+
@secure_session_only = options.delete(:secure_session_only) { false }
65+
super(app, options)
66+
end
67+
68+
6369
private
6470
def get_session(request, sid)
6571
logger.silence do
@@ -136,7 +142,7 @@ def get_session_with_fallback(sid)
136142
if sid && !self.class.private_session_id?(sid.public_id)
137143
if (secure_session = session_class.find_by_session_id(sid.private_id))
138144
secure_session
139-
elsif (insecure_session = session_class.find_by_session_id(sid.public_id))
145+
elsif !@secure_session_only && (insecure_session = session_class.find_by_session_id(sid.public_id))
140146
insecure_session.session_id = sid.private_id # this causes the session to be secured
141147
insecure_session
142148
end

test/action_controller_test.rb

+23
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,29 @@ def test_session_store_with_all_domains
299299
end
300300
end
301301

302+
define_method :"test_unsecured_sessions_are_ignored_when_insecure_fallback_is_disabled_#{class_name}" do
303+
with_store(class_name) do
304+
with_test_route_set(secure_session_only: true) do
305+
get '/set_session_value', params: { foo: 'baz' }
306+
assert_response :success
307+
public_session_id = cookies['_session_id']
308+
309+
session = ActiveRecord::SessionStore::Session.last
310+
session.data # otherwise we cannot save
311+
session.session_id = public_session_id
312+
session.save!
313+
314+
get '/get_session_value'
315+
assert_response :success
316+
317+
session.reload
318+
new_session = ActiveRecord::SessionStore::Session.last
319+
assert_not_equal public_session_id, new_session.session_id
320+
assert_not_equal session.session_id, new_session.session_id
321+
end
322+
end
323+
end
324+
302325
# to avoid a different kind of timing attack
303326
define_method :"test_sessions_cannot_be_retrieved_by_their_private_session_id_for_#{class_name}" do
304327
with_store(class_name) do

0 commit comments

Comments
 (0)