How does rails/devise handle cookie sessions? - ruby-on-rails

I'd like to understand what's really going on when signing in a user with rails/devise.
I've created a minimal rails app, installed devise and created a User devise model.
Everything works fine, and when I log in (using remember me) I get a session cookie just as expected.
Now what's bugging me is : How does rails handle the session informations that the browser is passing through the cookie ?
I'd naively expect some information to be stored in the database, but I don't see where. There's no such thing as session table, no session column in Users, and I couldn't find anything of interest in the tmp dir.
Note that restarting the server wouldn't kill my session. It is of course expected, but now I'm really wondering what kind of magic is happening here ?
in other words : how does the server check the validity of a cookie to authenticate a user ?
Thanks !

The default rails session storage is CookieStore. This means that all the session data is stored in a cookie rather than in the database anywhere. In Rails 3.2 the cookie is signed to prevent tampering, but not encrypted. In Rails 4 it's generally encrypted by default. The fact that it's in a cookie is how it persists across restarts of your server. It also means you can only store 4k of data and you wouldn't want to store anything sensitive in there in Rails < 4. It's best to keep a minimum of data in the session anyway.
You can also opt for storing the session data in the database and only having a session id in a cookie.
This answer I gave the other week has some extra info that might be useful:
Sessions made sense to me before I started reading about them online
Also, the rails api doc for CookieStore gives a nice summary:
http://api.rubyonrails.org/classes/ActionDispatch/Session/CookieStore.html

Related

Rails app with different session store for each model

I have two models doing login (Devise) in my Rails app - Admin and User, both currently use the default cookie store for session data.
I want to be able to identify an Admin session in AJAX requests coming in from the admin, for authorization of these API calls. I plan to do this by setting an encrypted cookie upon Admin login. When the AJAX API call comes in, I open the cookie, grab some identification from it and look for a matching existing Admin session in the store.
As I understand it, to do this, I must have session information stored in the back-end, either by DB or memcache stores.
I expect to have millions of sessions of Users and just a few sessions of Admin at any given time. For this reason, I don't want to just move all session information to a DB or memory, since this is a heap of unneeded data to store. I only want to store/look at Admin session data.
A solution will be creating some custom model which enumerates Admin user sessions, and is maintained by the app. This is simple enough but requires for instance, a way to clean up sessions when they die without signing out. Essentially this is an attempt to duplicate Rails's session store mechanism, which comes with all the problems of storing and maintaining sessions. Instinct tells me to avoid this solution. Am I correct to avoid it?
If so, then my question is, is there a way to configure multiple session stores in a Rails app, a different store for every logged in Model? In this case, have Admin sessions stored in memory, and User sessions stored in cookie. If not, I'll greatly appreciate any comments and suggestions.
Thanks!
You may be thinking about it wrong.
Session are a low level mechanism that you build your authentication on top of. Its just a cookie containing an identifier (a random hash) which is linked to a session storage (by default cookies). This is a simple mechanism to add persistence to a stateless protocol.
Confusingly we also use the concept "sessions" when talking about authentication - for example logging a user in is often referred to as "creating a session". This is complete poppycock as we are just storing a claim (often a user id) in the session that was created when the user first visits the application.
If so, then my question is, is there a way to configure multiple
session stores in a Rails app, a different store for every logged in
Model?
No. Thats a chicken-vs-egg conundrum. In order to know which session storage to use you would need to access the session storage to know which session storage to use... you get the picture.
While you could create your own session storage mechanism that works differently does this is most likely a complete waste of time. Premature optimization is the root of all evil.
As I understand it, to do this, I must have session information stored
in the back-end, either by DB or memcache stores.
Not quite true. You can perfectly well build an authentication solution with just the cookie storage. In that case Rails just keeps a record on the server of which session identifiers are valid.
The main reason you would need to store additional session information in the database or memcached is if you need to store more data in the session than the 4093 bytes allowed by a cookie. Cookie storage is after all much faster and does the job fine 99% of the time. YAGNI.
You should also recognize that not everything needs to be saved in the session storage. For example the Devise trackable module saves log in / out timestamps on the user table as part of the process of authenticating a user. This is "session information" yet has nothing to do with session storage.
I want to be able to identify an Admin session in AJAX requests coming
in from the admin, for authorization of these API calls.
There are many ways to use different authentication logic for different parts of the application such as Warden strategies. For an API you may want to consider using stateless (and sessionless) authentication such as JWT.

Rails secret token

I'm quite confused what is secret_token used for in Rails. Can anyone explain what it is used for? Is it OK to put this token in a public source repository and use it in production, or I should change it before deploying my app to prevent some kinds of attacks?
Answering my own question - secret_token is used to prevent cookie tampering in Rails. Every cookie has a checksum saved with it, so users won't modify cookie contents (and change saved user id to steal someone's account, for example). The checksum is based on cookie contents and secret_token, so if you are using cookie based sessions you should always make sure your secret_token is really secret, otherwise you can't trust that anything you put into session came back unchanged.

How do I prevent Rails users from accidentally authenticating as the wrong user?

Specifically, I have written a Rails app in which I'm using the default (in Rails 2.3.5) CookieStore session store and I've spotted an odd problem in development.
Myself and a few others had been using the site for a few weeks and we each had a login based on a username and password (each user registered themselves and I stored the (salted and hashed) data in the database). I was storing the user ID in the Rails session object (and, therefore, in the cookie that is passed back and forth between browser and server).
One important point here: since this is an intranet site, I set the cookies to stay alive for up to 2 weeks to avoid users having to log in all the time.
Today I reset the database, wiping all user records (and all other data, intentionally). A few users started registering themselves again and then one user found that the first time they went to the site since the wipe they were automatically logged-in as a different user!
I think I can see why this happened: the user ID passed from that user's browser to the server now matched a different user-record in my database. My initial thought was "oh dear, I wasn't expecting that!" but the more I thought about it the more I realised this was probably expected behaviour.
I realise I can change my Rails app to user ActiveRecordStore but before I did that I wanted to make sure I understand what's going on here. Specifically, does the combination of using CookieStore sessions and having the sessions stay alive for some time really create such a gaping security hole? Or am I missing something? Should the session_id be providing a little more security here?
The big security hole in this setup isn't the cookie length, it's setting the user_id in a cookie. This means that anyone who logs into your site can log in as anyone else just by changing that cookie! A hacker would just sequentially walk through user_id's, logging in and seeing if there's anything they want to steal or abuse.
If you want to roll your own authentication, try this instead: add a "token" string field to your user table. When somebody logs in, set this token to a random set of numbers and letters, and pass that as the cookie back to the user. The token should be at least 32 characaters, alphanumeric, upper and lower case.
Now when a user goes to a page, their account is looked up by that hash instead of their user_id. The value is that the hash is much harder to guess, and will never be repeated. Your user_id's were actually repeated when you reset the database, causing people to be logged in as each other.
UPDATE
#shingara is right that the cookie store does handle the security part already, my mistake. The user_id mixup is therefore a one-time occurrence because you reset the database. This is not a problem you'll face in a production environment, unless you reset the database again. If resetting is ever a possibility, then still do the token creation as I recommended. Otherwise, you're fine.
The simplest solution to the problem you had here would be to have changed the cookie name when you reset the database. The cookie name should be in config/initializers/session_store.rb
ActionController::Base.session = {
:key => '_your_app_session_v2',
You could also change the secret, but that may generate errors for your users if they request the site with an old cookie.
You case arrived only if you have 2 differents user with the same user_id. So it's not possible if you define the user_id like unique.
Another case, you can add in session, an hash with an unique key by user. when you check the session you get the user_id and check if the user_token is same . If not, the user is not authorized.
Thankyou for all the responses. They all answered my question in a way: yes, my setup (and my not setting a new session key after wiping the users) creates a security hole.
Lots of Rails tutorials advocate this setup without mentioning the fact that all you need is to monkey with your cookie to be fully authenticated as another user.
So, to summarise, I asked the question because I couldn't find anything discussing the danger of CookieStore session + long cookie lifetimes, and I found that surprising so thought I might be missing something obvious.
I had a similar issue and resolved it using a code snippet similar to
this comment by mdesantis on managing Rails secret token

Rails, CookieStore vs ActiveRecordStore

I am currently experiencing a strange issue with our users being logged out. I haven't been able to reproduce it explicitly.
The Rails application is using the default CookieStore.
My initial hypothesis is that somehow the session data within the cookie, or even the cookie itself is being destroyed. This may be either from a user clearing browser data, or something within the system that has not been caught.
As of now, the authentication system appears to be functioning as intended (Authlogic), and we are not experiencing the issue wide-spread in other components of the application.
I am considering using ActiveRecordStore to see if the problem is resolved. My understanding is the session data would be stored within the database, and if a cookie was being removed - the user would not get logged out.
Are there many known pros/cons to using CookieStore vs ActiveRecordStore?
Why is CookieStore the default when creating a Rails application, and not ActiveRecordStore?
I can answer your last two questions.
You should not use the cookie store if you're storing sensitive data in the session because you want such data to be on the server-side and not on the client.
The cookie store is the default because Rails is giving you a strong hint that you should not be storing lots of data in the session, by virtue of the fact that cookie storage is limited to 4 KB.
I think CookieStore is the default because it is simple. It doesn't require a database table.
CookieStore is not as secure as ActiveRecordStore. With CookieStore, intercepted cookies will give access to a valid session forever, even if you create a new one. With ActiveRecordStore, you can invalidate a session by removing it from the database.
See this blog post: http://www.bryanrite.com/ruby-on-rails-cookiestore-security-concerns-lifetime-pass/

Rails sessions current practices

Anyone have any "best practices" tips for Rails and sessions? The default session type for Rails 3 is still CookieStore, right? I used SqlSessionStore for a while and it worked well, but I may move away from that in favor of CookieStore.
Is it still not a good idea to use CookieStore for sensitive info, even with salted info or is that better stored in the DB?
Use the database for sessions instead of the cookie-based default, which shouldn't be used to store highly confidential information
Create the session table with
rake db:sessions:create
Run the migration
rake db:migrate
Make sure you also tell rails to use ActiveRecord to manage your sessions too.
Rails 3
config/initializers/session_store.rb:
Rails.application.config.session_store :active_record_store
Rails 2
config/environment.rb:
config.action_controller.session_store = :active_record_store
Cookies are encrypted by default in Rails 4
In Rails 4, CookieStore cookies are encrypted and signed by default:
If you only have secret_token set, your cookies will be signed, but not
encrypted. This means a user cannot alter their user_id without knowing your
app's secret key, but can easily read their user_id. This was the default
for Rails 3 apps.
If you have secret_key_base set, your cookies will be encrypted. This goes a
step further than signed cookies in that encrypted cookies cannot be altered
or read by users. This is the default starting in Rails 4.
If you have both secret_token and secret_key_base set, your cookies will
be encrypted, and signed cookies generated by Rails 3 will be transparently
read and encrypted to provide a smooth upgrade path.
Active Record Session Store is Deprecated in Rails 4
This answer is now out-of-date with regard to Rails 4. The Active Record
Session Store has been deprecated and removed from Rails, so the following
generators will no longer work:
rake db:sessions:create
rails generate session_migration
This was pointed out in this answer. The reason that the Active Record
Session Store was deprecated is because the reads/writes to the database don't
scale well when you have a large number of users accessing your application, as
stated in this blog post:
...one major issue with the Active Record session store is that it is not
scalable. It puts an unnecessary load on your database. Once your application
receives a large amount of traffic, the sessions database table is
continuously bombarded with read/write operations.
As of Rails 4, the Active Record session store has be removed from the core
framework and is now deprecated.
If you still want to use the Active Record Session Store, it's still available
as a gem.
Current Rails Session Best Practices
For more current best practices for Ruby on Rails sessions, I advise that you
check out the lastest versions of the Ruby on Rails Security Guide.
I don't believe anything has changed in how anyone on any platform should handle cookie based sessions. Be skeptical of anything that passes beyond the server's control (cookies, form posts, etc.) Thats a general principle of web development.
As far the encryption, I don't know if anything has changed on that front.
Something to be mindful of with a cookie store is the limit to the amount of data, and the gotcha that this data will be sent on the wire in every request, where as a database store only transfers the id and the data lives on the server.
FWIW, rails 3.1 suggests running
rails generate session_migration
However this generates the exact same migration as
rake db:sessions:create
The Rails defaults seem pretty good to me- The CookieStore is fast and should cover the majority of use cases. Sure you're limited to 4kb and your data will be visible to the user, but the Rails way is to only use session for things like integer IDs and basic string values- If you're trying to store objects or highly confidential information in session you're probably doing it wrong.

Resources