There are a couple of issues with the "remember me" function that lead to it giving the appearance of being unreliable. This probably has flow-on issues such as resistance to rolling out MFA as it relies on this function for a good UX.
In my view, the first scenario has the higher impact, but that's a bit subjective.
Steps:
Expected:
Actual
I believe that when the "remember me" box is not checked, it is clearing out any ALC token from all devices. It would make sense to clear out the ALC from _this_ device, but not other devices.
From my perspective, this is a bug, without any security implications.
Steps:
Expected:
Actual
The "log out" functionality is hard-coded to perform "log out from all devices". This has some security benefit but doesn't make a lot of sense as a default.
From my perspective, this is a bug, but it _does_ have some security implications. Ideally, the log out action (at least in the CMS) would pop up a box with a "log out from all devices" checkbox, defaulting to unchecked.
true/false configuration value)Regarding scenario 2, one of the options we are investigating is device management in the users profile. The "log out from all devices" is a good solution that I'll add to our list of possibilities, it doesn't let the user know which devices or how many devices have tokens.
Regarding scenario 2, one of the options we are investigating is device management in the users profile.
Yeah that would be a more complete solution, I agree.
I'm anticipating that both of these scenarios would be resolved with the inclusion of the following module, which we are enhancing to be the baseline for device/ session management within the CMS UI. I'll do some testing this afternoon to confirm.
That might still leave a gap where these bugs are present in framework, but we've also talked about making the session-manager module a requirement of the security-extensions module and making that a requirement of core.
Code which is probably leading to everyone getting logged out:
Easy solution to this is probably to just change the config value of logout_across_devices from true to false
I tried replicating both scenario with Chromium and Edge:
CookieAuthenticationHandler handles the authentication of the Member and the creation of cookies on the user's device.RememberLoginHash is a DataObject that link up the DeviceID, the Remember me Hash and the Member. When the user come back without a session, CookieAuthenticationHandler links back those 3 piece of data to let the user back in.This behaviour is triggered when a user logins and checks the "remember me" option.
CookieAuthenticationHandler::logIn() is calledRememberLoginHash. *RememberLoginHash::generate() is called for the provided $member. This automatically generates a device ID (sha1 hash) and a member token (sha1 hash, stored encrypted with the Member's Salt). An expiry date is also set. *RememberLoginHash::force_single_token' is set to true, any otherRememberLoginHash` for this user well be deleted (default is false)RememberLoginHash token UNencryted (TTL defined by RememberLoginHash::$token_expiry_days, defaults to 90 days)RememberLoginHash device ID (TTL defined by RememberLoginHash::$device_expiry_days, defaults to 365 days)* You can't have multiple "remember me" session on the same device
* Encrypting the member hash with the Member's Salt will invalidate the "remember me" hash when the user changes their password. Encryption is done via $member->encryptWithUserSettings().
** These 2 cookies are unrelated to PHPSESSID which contains the ID for the current session and is only valid for the duration of the browser session.
This workflow is triggered when a user returns to the site after having logged in with the "remember me" option, but having close their browser, therefore invalidating their session.
CookieAuthenticationHandler::authenticateRequest() is called with the HTTPRequestRememberLoginHash matching the hash, the device id cookie and the member IDRememberLoginHash is not expired.logIn function on the Identity store provided by CookieAuthenticationHandler::CascadeInToRememberLoginHash::renew()RememberLoginHash is written to the DB with the new token hash *RememberLoginHash::$token_expiry_days.* The purpose of this step is to make sure you can't reuse the same token cookie multiple times
** The cookie time TTL seemed a bit weird here. The token cookie TTL gets extended after each new session, so it will live past the RememberLoginHash expiry date. The device ID cookie never gets extended, but its TTL is 365 days vs RememberLoginHash's 90 days, so this cookie will also outlive RememberLoginHash. The key thing, is that the expiry date on RememberLoginHash never changes. So no matter what, it will eventually expire even if the cookies are still in the user's browser.
CookieAuthenticationHandler::logIn() is called.RememberLoginHash::clear() with the current member and the device ID.RememberLoginHash::$logout_across_devices is false and a device ID was provided *RememberLoginHash object for the provided member and device ID.RememberLoginHash object the current member* I didn't try this, but I get the impression this logic is buggy. If logout_across_devices is false and you try to logout from a non-persistent device, you won't have any device ID cookie, which will cause all your RememberLoginHash to be deleted.
@clarkepaul Could use some feedback on this.
We could:
RememberLoginHash::$logout_across_devices to falseRememberLoginHash::clear() so that nothing happens if no device ID is provided.In that scenario, there's no way to invalidate a "Remember me" cookie on a lost device, short of changing your password.
Basically this would give the option to user to explicitly clear all their RememberLoginHash objects
RememberLoginHashThat's probably a very bad idea. It would allow users to delete specific remembered device. However, the only identifier we have for those device is a very unfriendly sha1 hash.
Plus it would duplicate a the logic currently being implemented in session manager.
If we're serious about making session-manager a supported module and shipping it in our default recipe, then maybe doing nothing here is the best approach long term.
Session manager sounds like a better place to have a "logout everywhere" feature. It would also allow you to invalidate individual session directly.
So we could do this:
RememberLoginHash::clear()RememberLoginHash::$logout_across_devices defaultRememberLoginHash::$logout_across_devices to false ORMy feeling here is we're investing in improving the session-manager module with the goal of improving user and site security of the CMS. The best way to make that become a reality is to make that module as easy as possible to adopt.
We're still to confirm how the session-manager work will be released, but I'd like to see it go into the CMS core recipe so adoption is as pain-free as possible and requires one of:
Given the above, I think we should consider the investment already going into the session-manager module and take the "minimal fix" approach to resolve this issue.
In that scenario, there's no way to invalidate a "Remember me" cookie on a lost device, short of changing your password.
The best answer here is to use the session management feature that we've enhanced exactly for that purpose.
If we weren't investing in the session manager already then I'd be all for the minimal fix. With the session manager module we have less need for the logout of all devices鈥攏ot the most important addition unless its needed technically but adds a little value.
+1 for seeing this in core via the session manager module as it will provide the most benefit to the most people, makes it easier writing guides and better usability all round.
Do we have any idea how this might affect projects upgrading using the likes of SSO or identity managers? Is it worth checking in with a few projects to see howe this might affect them just so we have it in mind?
Do we have any idea how this might affect projects upgrading using the likes of SSO or identity managers?
No confident idea at this stage. We've got a dedicated issue to run through test scenarios with SSO in place: Compatibility with SSO modules.
With the session manager module we have less need for the logout of all devices鈥攏ot the most important addition unless its needed technically but adds a little value.
Totally open to raising a separate issue to discuss including a 'sign out of all devices' feature if you like that idea @clarkepaul? I just think it should be handled as an additional and not be relied on to close this issue :P
@brynwhyman nope I don't think we need a "sign out of all" thing, as you were :)
It seems like the "Do mostly nothing" solution is the favourite. I'll confirm that setting RememberLoginHash::$logout_across_devices to false behave as expected and I'll review the existing doc.
My guess is this makes most of the ACs moot since we're going to keep the existing default.
Just clarifying that "Do mostly nothing" solution involves keeping the current default for RememberLoginHash::$logout_across_devices and the current behaviour.
The rational for this is mostly one around comms in the next release. If we change the default now, we'll have to explain to developers upgrading the ins and oust of the new behaviour. Then in the next release, we'll probably ship session-manager in core, which will again change the logout behaviour. I don't think there's much value in changing the default if that default is only going to matter for one release cycle.
Also the current default, is arguably more secure since it invalidates all your "remember me" tokens when you logout.
My thinking is this:
$logout_across_devices to false if you want the better log out behaviour.$logout_across_devices to false. If you logout, only the "Remember Me" token for the current session will be invalidated.$logout_across_devices, when you use session manager to terminate a specific session, any "remember me" token associated to that session must be invalidated.Sounds good
I've created a card to add unit test to the remember me feature: https://github.com/silverstripe/silverstripe-framework/issues/9896
Agree with Max and Bryn (and the "Do mostly nothing" option): We're fixing this properly with session-manager, and we don't have enough capacity to also fix anything in core without session-manager.
In terms of making this part of the core recipe, I think we need to have a broader discussion about what that means for support expectations from Silverstripe Ltd. That's already on Bryn's radar.
Linked PR has been merged
Most helpful comment
Just clarifying that "Do mostly nothing" solution involves keeping the current default for
RememberLoginHash::$logout_across_devicesand the current behaviour.The rational for this is mostly one around comms in the next release. If we change the default now, we'll have to explain to developers upgrading the ins and oust of the new behaviour. Then in the next release, we'll probably ship session-manager in core, which will again change the logout behaviour. I don't think there's much value in changing the default if that default is only going to matter for one release cycle.
Also the current default, is arguably more secure since it invalidates all your "remember me" tokens when you logout.
My thinking is this:
$logout_across_devicestofalseif you want the better log out behaviour.$logout_across_devicestofalse. If you logout, only the "Remember Me" token for the current session will be invalidated.$logout_across_devices, when you use session manager to terminate a specific session, any "remember me" token associated to that session must be invalidated.