The typical way beginning Rails developers (including me, on my first Rails apps) would implement logins is stuffing the user ID in session[:user_id] and setting the currently logged in user to that when it is both present and in the DB.
It is true that this approach will make sessions replayable. It will also:
1) Make it impossible to reliably expire session, since the user's browser is not under your control and may treat the expiration date as advisory only.
2) Make it impossible to forcibly expire session, which you'll want to do in event of a user changing or resetting their password, changing their username/email address, etc. (Generic security advice for all web apps, by the way. Otherwise compromise of user accounts, via losing password, XSS, or what have you, is impossible to reverse.)
3) Makes it impossible to implement Sign Me Out Of All Devices, which is a very desirable capability to have for many applications. For example, if you have a more-important-than-cat-videos app, somebody losing their phone should probably not result in their account on your service being irrevocably compromised. You could provide a Dropbox-esque UI to manage their state across multiple devices or, in a pinch, just say "If you hit the log out button on any device, we log you out of all devices."
4) The simplest possible Rails login system is facially non-compliant with a number of security regimes you might be under, including HIPAA among others.
n.b. Switching to ActiveRecordStore doesn't by itself solve anything but #1. You do need app-side logic.
2) in the case of a compromise you can change the app secret. That's a pretty big sledgehammer and will log out all users from the app, but it will reliable smash that fly.
1) could be tackled the way gitbub proposed: just include the expiry date in the signed cookie. Discard any cookie that's past expiry.
3) could be tackled by using some sort of per-user cookie secret. That however would require a storage again and then you could as well store the whole session in said storage. Not much to win here.
http://stackoverflow.com/questions/11281141/after-logout-if-...
(I was quite surprised a long while back :-)
The best remediation is to include an expire timestamp within the content of the signed cookie and to check this on the server - you can't rely on the client deleting the cookie (never trust the client).
The guys at GitHub fixed this particular issue in their rails stack, and submitted a pull request 4 months ago:
https://github.com/rails/rails/pull/11168
It started out well, and their proposed solution would have fixed this problem. The thread got bogged down in discussing backwards compatibility and the original submitter just gave up on it.
I hope this might trigger somebody to pick that pull request up again and get it sorted and merged.
Since signed cookies are now a popular form of persisting session state I have been meaning to run through all the popular web app frameworks to check this issue.
edit: check out TimedSerializer from the itsdangerous Python library that Flask uses:
https://github.com/mitsuhiko/itsdangerous/blob/master/itsdan...
Giving up. The inheritance and mixins approach in the chained and legacy cookie jar code is absurd beyond my patience.
Someone else is free to run with this code as a start and take the credit.
[1] https://github.com/rails/rails/pull/11168#issuecomment-22353...
[1] - https://docs.djangoproject.com/en/dev/ref/settings/#std:sett...
The shared secret is the combination of a near-random string of bytes generated by a hash of numerous things, plus a unique ID generated upon insert into the database table. The secret survives in the table until the user chooses to sign out, or until the cookie expires. Once the secret is expired, the cookie is useless.
This also means users can go into their account page to see on which devices their account has been remembered, but also to log out any/all other sessions.
Isn't the point of using a cookiestore to avoid hitting the database and thus eliminating the need for the painful session/state maintenance across multiple nodes? Your solution looks more like a DIY security solution to me, (please correct me if I'm wrong) and I would take major precautions in gambling with security-related functions of a framework.
P.S I am confused, please explain if I am wrong, I will gladly rather be corrected than make a possibly serious security mistake in one of my own applications.
If session/state maintenance across nodes is painful, the solution isn't to trust the web browser, it's to make such maintenance much less painful. For example, you can use a cache server (like ehcache) to store session state. Or you can use smarter load balancers (like haproxy) to minimize node hopping.
A cookie is an authentication credential, just like a username and password combination. If you can't invalidate an authentication credential on the server side, your solution is an instant fail.
The author isn't telling people anything new, so it shouldn't take the form of an announcement.
>Separately, it is a good design for your Web app to require that the user supply their current password before changing sensitive fields such as password or email address. If the CookieStore-stored session were to be hijacked, the malicious user could change the user’s password:
I don't see why that's the case - presumably, if I'm requiring the user's password to perform sensitive account changes, then the hijacking user will not be able to perform those changes. It's not like the CookieStore contains a plaintext password or anything.
This is ultimately a simple replay attack, which any application using a server-side session would be equally vulnerable to. The effects of that replay attack are definitely worse with CookieStore, because a user cannot subsequently invalidate their session from the server side (although the app owner can obviously invalidate every session).
This could be solved relatively easily by providing a per-session token which can be invalidated, but at that point I guess you'd have to question why you're using a cookie-stored session anyway.
This is not a new issue. It is a well known limitation of the cookie store. I've been working around this with timestamps for years.
As well: rotating the secret token every night could probably be used to discard all existing cookies, right? (although this is a bit brute force and will kick out anyone currently using the app).
It's a three or four line before filter in the application controller. I reimplement it every time since it's relatively simple.
I don't have it on any public code since it's also the last thing I implement before launching.
Versions concerned: RoR 2.x, 3.x, 4.x
Description : Ruby on Rails contains a flaw in its design that may allow attackers to more easily access applications. The issue is due to the CookieStore mechanism storing cookies on the client side, while not maintaining a corresponding entry on the server side. When an application terminates a session, Ruby on Rails has no method to track this and truly invalidate the cookie with the default configuration. This means that cookies persist "for life" and can be used to access an application even after it is thought to be terminated in many cases.
Solution: Currently, there are no known upgrades or patches to correct this vulnerability. It is possible to temporarily mitigate the flaw by implementing the following workaround: switch to a more secure authentication management systems (e.g. ActiveRecordStore).
CookieStore does its own job correctly: it has an "httponly" option which prevents being stolen by injected javascript, and it checks signature which prevents content being modified or forged. But it's not CookieStore's job to prevent sensitive content being watched. It's HTTPS's job.
Changing session store to a database or memory store won't
fix it: a session id is still required on the client side,
then your session is still hijacked because the attacker
still gets the session id.
The bug specifically addresses logging out.When you store a record of the session on the server side (with the session ID you mention), you clear that session record during logout; the session is now gone, and cookie isn't valid anymore.
(With the cookie-only approach, the server will continue to accept a cookie that you wanted to have cleared.)
When I click log out, I know I'm logging out on this
browser but not other browsers.
Different browsers get different sessions. ... it doesn't limit you from storing and checking
things on the server-side.
Correct, but you basically end up reimplementing parts of ActiveRecord::SessionStore anyway.It looks like a fundamental design problem with a client side token... You can limit intrusions by adding an expires_at value to the cookie data. But I don't see any way to actually expire a token without some server side tracking.
Basically we still get benefits from storing the session data client side (much less db access) and only store minimal data on the server side.
Another issue is old sessions may be deactivated once the user logs out, but all those old sessions suddenly become valid again when the user logs in again, since the time stamp field is now present. It doesn't prevent the issue the author is highlighting (that sessions can last forever).
I guess it reduces the attack surface (the genuine user must be logged in at the same time as the attacker) but doesn't completely solve the problem.
Nowadays most services work only on HTTPS. In that case is not possible stealing the cookies observing the traffic but there is always malware able to grab the cookies. A mechanism to invalidate the session should always be available.
If you moving data back and forward on HTTP and you are using the cookie to store the variable in the session, there is always the risk to expose sensitive data stored in the session to the outside world.
I posted about this on HN originally here: https://news.ycombinator.com/item?id=6458433
Time to switch web hosts.
You are also clearly neglecting the fact that proper session cookies are _always_ cryptographically signed and cannot be tampered with, if properly implemented.
I agree that simply storing everything in the cookie is wrong, and Rails should never have been doing this - hell, even CodeIgniter (PHP) has DB based sessions.
But most Rails devs (myself included) are just about getting shit done, we don't know everything that's going on under the bonnet, and we don't want to.
And FYI CookieStore is just the default, because it's convenient and require no dependency. But you're juste a line of configuration away to switch the sessions inside your DB or Memcache or whatever.
I love getting shit done.
It's my job as a professional to know everything that's going on "under the bonnet."
I'm still learning.
But it's my job to try.
It's not that hard to just set up a Redis or whatever store to handle stuff like this, I never understood why people whouldn't bother.