sorcery
sorcery copied to clipboard
"Remember Me" functionality doesn't work when users access my app from 2 separate browsers
I've run into a pretty annoying behavior in which "remember me" doesn't work across browsers. Here's how to reproduce it:
- Log in on Browser A (remember me = true)
- Log in on B (remember me = true)
- Quit browser A
- Re-open browser A
You are now logged out of the application on browser A. Instead, you should remain logged in on browser A until you click "log out"
Isn't that normal since you are still logged in browser B?
"Remember me" means "keep me logged in on this browser until I click log out". I.e., the behavior of "remember me" shouldn't depend on what I've done on another browser / computer somewhere.
I feel very strongly that this is the correct behavior – this is how Gmail, Facebook, and Twitter all behave. In fact, I can't think of a single famous web application that has the current sorcery behavior. (Also, AuthLogic's "remember me" behaves in my preferred manner as well)
I think that's because sorcery saves a new remember_me_token to the DB on every "remember me" request, regardless on browser. This makes the remember_me_token in other browser cookies revoked.
The solution that comes to my mind right now is to create a DB "session" record for every login from every browser, with expiration date, instead of the single token per user record. When the user cookie is sent, the token is looked up in the sessions table, and the user id is retrieved and auto-logged in.
I think the solution is to save a new remember_me token only when the use changes his password, not when he logs in with a "remember me" request.
I.e.:
- Every user has a remember_me token
- Checking "remember me" when you log in tells the server to add this token to your cookie
- If you log into multiple browsers with "remember me", each browser gets a cookie with the same remember_me token
- When the user changes his password, reset his remember_me token as well (and put the new token in his cookie if the old token was there)
This is what authlogic and most websites do.
If you don't also keep expiration info on each browser, than the expiration will take effect from the time of the first browser, even if the last browser asked for "remember me" just a minute ago (unless you put a really big expiration time and then it won't matter).
Also, when using the same token until you change password, you increase the window of getting your token read by an attacker.
I found this link: http://stackoverflow.com/questions/2594960/best-practice-to-implement-secure-remember-me
and it made me think it might be a good security measure to generate a new token on each login via cookie. Will make the attack window even smaller. Of course, to make it work across browsers means keeping a record per browser.
Ok, so we have httpOnly turned ON (prevents javascript from reading it), the cookie is signed (prevents cookie tampering), and let's say we don't use SSL. So an attacker can now only steal your token by listening to your network traffic.
If we switch remember_me token on every login, even if an attacker gets your token by listening, it is already void.
If you don't also keep expiration info on each browser, than the expiration will take effect from the time of the first browser, even if the last browser asked for "remember me" just a minute ago (unless you put a really big expiration time and then it won't matter).
Right, the correct behavior is to have a large (and in fact functionally infinite) expiration time. If the user checks "remember me" he shouldn't be logged until he clicks "log out". This is how almost all websites work; e.g., I think I've been using the same Gmail "remember me" token on my iPhone for over a year
If we switch remember_me token on every login, even if an attacker gets your token by listening, it is already void.
Right; this is the current behavior, and while it's more secure, it's also more inconvenient for the user (/ not what they expect). (And if security is your #1 concern, you should just disable "remember me" functionality entirely – by implementing "remember me" a developer is saying "I want to sacrifice security for convenience because this isn't a banking website" or whatever)
and it made me think it might be a good security measure to generate a new token on each login via cookie. Will make the attack window even smaller. Of course, to make it work across browsers means keeping a record per browser.
I don't see how having one token per browser is better (or functionally different at all, actually) from having one global token. As you say, the big security issue is someone sniffing any valid remember_me token. So who cares whether the user's token is "cat" on Firefox and "dog" on Chrome; you can still own him by sniffing his traffic from EITHER browser and grabbing EITHER token, no?
No no, what I meant was, the token the attacker is sniffing right now, is already obsolete, because when the user sends me his token, I give him a new one on the spot. The attacker could also sniff for the new token sent from the server, but the window to use it will be very short if it is constantly replaced. This is a very secure remember me, though a bit taxing on the DB. Such feature will have to be managed per browser since each browser holds a different token at each time.
But that was just thinking out loud...
regarding Gmail, I'm actually logged out once a month on my browser, so it's not 9999 years expiration.
I do agree the whole feature is insecure by nature... I guess it will not be less secure than it is now with your suggestion.
However, I would like your opinion (anyone) on the ultra secure remember me feature.
No no, what I meant was, the token the attacker is sniffing right now, is already obsolete, because when the user sends me his token, I give him a new one on the spot. The attacker could also sniff for the new token sent from the server, but the window to use it will be very short if it is constantly replaced. This is a very secure remember me, though a bit taxing on the DB.
Ah – let me see if I understand:
- Client logs in with "remember me"
- Server gives him a remember me token (say it's "dog"), and a browser identification token (say it's "pepsi")
- On next request, client sends server token "dog" (and his browser identification token "pepsi"). Server sees that it's the same client and he's logged in with "remember me" and the request succeeds
- Server now gives the client a new token ("cat")
- Client makes another request with token "cat" (and the same browser identification token "pepsi"). Server sees that it's the same client and he's logged in with "remember me" and the request succeeds
- Server gives the client a new token again! ("house")
- If the attacker had sniffed "cat" before, it's useless. The only way an attacker can own the client is by sniffing the new token the server sends down BEFORE the client uses it in a subsequent request
This does seem like a more secure strategy if you're not using SSL. But if the server REALLY cares about security, it must implement SSL anyway, which kills the advantages of this approach – I mean, without SSL the client can still get SUPER-DUPER-owned by simply having his password sniffed, and the only way to defend against that is with SSL.
Either way, I think your proposed strategy could be a future feature, but that in the meantime you should change the behavior to reset the remember_me token on password change instead of log in since, though it is less secure, it's what the user expects / wants (and it's a much simpler change).
On second thought, if I needed secure login I would just use SSL... :-)
Yes, your algo description is correct, and I've (finally) reached the conclusion that SSL is the answer...
Alright, it will be fixed when I get around to it. Pull requests are also welcome, as always ;-)
I feel like the current approach and what people write on the internetz is too model-centric. I used authlogic previously and it was even more confusing by introducing an additional UserSession class. Why not simply use Rails session for the remember me functionality? This is an ideal application for the session construct. They've done all the hard work for you. As always it's best to use a database-backed session, especially if you are already using authentication (hopefully with SSL). In your before_filter you check last_login_at with your remember-me period to decide what to do. After that you can execute require_login and everything works as always. You don't even need adding a remember_me field to your model. It can be "volatile" in the session, but resurrected from login to login. If you find ActiveRecord session store slow, then you can use Cache or Redis store.
A more appropriate term would be server-based session :)
*server-backed
I looked at the code yesterday and noticed that session_timeout is doing something similar, but unfortunately the current implementation doesn't allow me to leverage it to implement a session-based remember_me and inactivity timeout at the same time. When you have config.session_timeout_from_last_action = false it doesn't track last_activity in the session.
Any progress with this? I'd also like to see Sorcery provide better support for users who wish to be remembered in multiple browsers (on multiple devices). That seems like a pretty standard feature these days.
Bump, this would be ideal like @mokolabs mentioned when you have users accessing from multiple devices it would be nice for remember_me to work for multiple sessions
@arnvald Any updates on this issue? I'll implement a workaround in my own project if not, but don't want to duplicate effort if it's coming soon...
Hi @bfischer1121, I want to do it in August, but I can't promise if I manage to.
@arnvald Let me know if you need any help. I would love to see this get added to Sorcery.
@NoamB @arnvald Any news? I will need this functionality soon for a client's project. If you're not already working on it I would be glad to do it myself, following your guidelines. Let me know, thanks!
This seems like it would be useful functionality. Has there been any further progress since this discussion?
@MartinodF @stephenorr @arnvald I would also be down for helping out.
@arnvald I'd like to help out in any way possible to get this out the door. Thanks for the great gem.
@bfischer1121 @mokolabs @MartinodF
if any of you wants to take this task, I'll be very happy to help. A few months ago Devise introduced some solution, I think it's worth checking it to see if it works well and if it's easy to implement in Sorcery.
I’ve created a Gitter room for Sorcery: https://gitter.im/NoamB/sorcery - feel free to ask me any questions there.
To all waiting for some progress on this: I have implemented support for persisting the login token as a configuration option (default behavior remains unchanged). See my pull request here with full details: #690. Please use and test in your apps to see if it solves your use case.
Here's a little background on the pull request from @rubiety.
We've been using sorcery for a few years on Graffletopia, and the lack of support for multiple "remember me" browser sessions has always annoyed me.
So I thought this would be a good opportunity for us to contribute back to the sorcery project by having @rubiety devote some time to fixing this issue.
Please let me know what you think!