sorcery
sorcery copied to clipboard
Rails 4.1 can connect with cookie copy/paste even after reset_session
Hi,
We got a problem on our website, once a user login, we can copy the cookie (for example with EditThisCookie on chrome), logout, then through the extensions we paste the cookie, reload the page and we are logged in (session fixation attacks?).
We tried to use reset_session as said in http://guides.rubyonrails.org/security.html#session-fixation but it didn't work, I still can login by pasting the cookie.
Here is what I do in the SessionController#create.
if login(params[:email].strip.downcase, params[:password], true)
reset_session
end
We are using ruby 2.0.0p247, rails 4.1, sorcery 0.8.6
This session store is set to :cookie_store
I also tried using reset_sorcery_session instead of reset_session, same_results.
Hi @nekogami thanks for your report. It's a serious issue and I'll to find out the reason, but it might be beyond my knowledge of how sessions work in Rails, therefore any help will be appreciated. /cc @kirs
Hi. Actually this is not a bug, it is how sessions in Rails work. Usually you store user_id in session. When you log out, session's user_id parameter gets cleared. If you copy first state cookie (with user id) and set it after log out, you put user_id back to session. And it is not considered as serious bug, because if you have access to logged in session, you already have an access to account. Also it is really possible to do replay attack, but for session it's not so dangerous as such attacks are considered to be carried out by logged in user himself.
If you find this really important it should be simple enough to add a token to the user table, change this token every sign-in/sign-out and on sign-in add the value (or a hashed version of the value) to the session. Hook into the authentication before filter, or add an extra before filter, to check the value of the session (cookie) with the value in the database, if they are not the same kill the session.
This would have the added benefit/side effect that logging out will log you out across all browsers / computers you are logged in at.
In most cases it is not a benefit. Usually it's annoying: if user has two trusted and one untrusted computers, when he is logged out on untrusted one, he will have to input login/password again on trusted too? Of course with banking applications such paranoid system will be the best choice (although they usually reset sessions every time you close browser), but for regular web application it's overkill. One solution is to keep session identifier in additional database table, on log in do
ss = Session.create(user_id: @user.id)
session[:ssid] = ss.id
and do Session.find(session[:ssid]).destroy
when user logs out. Then we can do something like:
@current_user = if Session.exists?(session[:ssid]) && session[:user_id]
User.find(session[:user_id])
else
# reset session if it includes user_id, otherwise leave it as is
# (changing session on every request is not good too)
end
We checked this a bit more yesterday with @Borzik. There are a few things to note here:
- no matter what security we add to Sorcery, using SSL/TSL is recommended to prevent attacks
- as Felix wrote, this behaviour is just how cookies work in Rails. Devise has the same issue, although it allows to invalidate all sessions by changing password. Authlogic stores sessions ids in the database, therefore it prevents these attacks
We can do 2 things here:
- just like Devise does, we could add some random string to the session id, and store this string in database. However we'd let developer decide if and when to change that string (either on logout or on password change)
- we could add mechanism of authentication strategies. This requires some work, but then developers could write plugins with custom strategies, e.g. sessions stored in db or authentication tokens.
Ok thanks for the help, I think I just misunderstood what was said in the documentation about that (to me reset_session was supposed protect from a copy/paste of the cookies somehow)
Much more logic now that I've read the comments ^_^" As for the solutions, I thing introducing a strategy pattern would be awesome and the most extensible.
Probably it would be good to have a special submodule, which handles the following:
- Generates auth_token for the user in the db
- Stores that auth_token in user session
- Checks that tokens in db and in the session are matching as a part of auth workflow
- Has a method to reset the token in db, like logout_all (which will make all sessions invalid and logged out).
In that case developers have different options to implement workflow they need, e.g. they can logout all sessions when user changes the password or they can always logout all sessions when user clicks logout on the website or they can implement special link in the profile "Logout all sessions" - it's up to them.
We can take a look at how gmail handles that for example. It has logout link which just logs you out in the current browser, but you are still logged in other browsers/computers and there is a link "Logout everywhere" Which actually resets that auth_token and logs you out everywhere.
Yes, all sites should be under https, but most part of the internet is not and we should take it into account and try to improve security for that. The usual thing is: you are in a cafe and using free wifi. It's super simple to steal browser cookies for an attacker via network sniffing and if auth solution does not have the way to reset all sessions (I mean the sessions of that user in different browsers) globally then attacker will get the access to user's account forever, even if he changes his password.
That is actually pretty critical security issue.
Please let me ask you a question because I'd like to use sorcery for our incoming project.
As far as I understand, since this security risk occurs only when using the default cookie as session store, you can change it to an alternative backend session store (e.g. active record or kvs) via gems like activerecord-session_store, at least before you improve the security.
Sorcery works with backend session store like the above ones, doesn't it? Sorry but I haven't tested it yet though.
sorry @5t111111 didn't check my notification recently.
For your question, yes it does work, we ended up swiping cookie_store with the gem redis-session-store https://github.com/roidrage/redis-session-store (we didn't want to put any more load on the DB instance), it works perfectly it shouldn't have any problem with the active record store.
@nekogami Thanks for your response. I've personally confirmed active record session store and found it works flawlessly so far. Glad to hear about redis-session-store because I think I should go redis for the same reason as yours.
Its worth mentioning that github describes their solution here: https://github.com/blog/1661-modeling-your-app-s-user-session
@arnvald @NoamB any update on that? Probably it's worth mentioning that this issue is not only Rails 4.1-related, but it's a major security issue. @sirwolfgang referred really nice description from GitHub's team.
@dhampik I doubt anyone is working on it. I won't have time to do it myself at least in the next few weeks, but if you think you can implement it, that would be great.
The easy way would be just to change session key from user id to some custom token (stored in database) and adding logout_all_sessions
method that would reset this token.
Storing sessions in db is another good idea, but I think it requires way more work. If anyone feels they can do it, I'll be happy to help.
Hi, we ended up stocking session inside a redis through https://github.com/redis-store/redis-rails since we had a tight roadmap to follow and didn't have time to explore and modify sorcery.