Background information about session storage in Rails
Rails has a
default mechanism to store the session
Show archive.org snapshot
in the
CookieStore
Show archive.org snapshot
. This is a cookie which holds the entire user session hash in the browser. This cookie is serialized, encoded with base64, and signed.
How Devise handles authentication
Devise uses this CookieStore
. To track a users session, a salt is stored in the session cookie when a user logs in.
When a user logs out this CookieStore is overwritten and current_user
is set to nil
.
But what about the old session cookie?
This session cookie is still valid! If this cookie is stolen before it gets overwritten, it can be reused to get access to the application again.
How can we invalidate old session cookies on logout with Devise?
1) Reset password
The only option provided per default by Devise is to change the password of a user. The salt stored in the session cookie by Devise relies on the password. So if you want to logout a user AND invalidate old session cookies you need to reset her password.
2) Extend the salt with a token
Add a session_token
column to your devise model (e.g. User
) and override Devise #authenticatable_salt
method to contain your session token:
class User < ApplicationRecord
devise :database_authenticatable, :recoverable, :rememberable
def authenticatable_salt
"#{super}#{session_token}"
end
def invalidate_all_sessions!
update_attribute(:session_token, SecureRandom.hex)
end
...
end
You can now invalidate the session cookie by resetting the session_token
of a user, when she logs out:
class Users::SessionsController < Devise::SessionsController
def destroy
current_user.invalidate_all_sessions!
super
end
end
Note that changing the session_token
will invalidate all session cookies of this user. If she is logged in on other devises (e.g. tablet, mobile phone, ...) she will be logged out on all of them.
3) Switch to a persisted storage for sessions on the server
Another option to prevent this session replay attack is to switch to persisted storage for sessions. Therefore you can use either a database, memcache or redis. (e.g. with
ActiveRecord::SessionStore
Show archive.org snapshot
)
But this means, you need to take care of adding, accessing, and removing the session data. This may has an impact on performance on high traffic sites since a session may be allocated even for anonymous browsing traffic.