-
-
Notifications
You must be signed in to change notification settings - Fork 9.6k
Session lost due to concurrent requests with "Remember Me" #28314
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
Would you like to work on a PR that would follow #27427 (comment)? |
I would, is preferred solution to skip authentication of already authenticated user? |
Any hint @weaverryan or @chalasr, etc.? |
Do I understand this correctly that with “Remember Me” enabled, the session ID changes for every request? Asking because I am chasing spurious failures on a CI where concurrent requests (headless browser/AJAX) lead to lost sessions. |
@mpdude yeah that seems to be the case. I'm running into the problem that I have an ajax request that runs every 5 seconds in the back-ground and if the user changed pages before that request finishes it will never get the new value for the remember me cookie so therefor it sends the old value which then Symfony sends a new set-cookie header with the value "deleted". It really sucks that this functionality cannot be disabled and is on by default. I could make the ajax request blocking but that isn't really a solution and is just a gross work-around. Would be nice if ajax requests were ignored for updating the remember me cookie. |
If you duplicate the service and remove the logic that generates a new token on every request it fixes it. I just posted a blog entry on my website with how to do just this: https://skylar.tech/fix-for-concurrent-requests-breaking-symfonys-rememberme/ I still don't understand why the I think the documentation should be updated and this "feature" should be able to be disabled/enabled without overwriting the service. EDIT: |
I opened #41175 which aims to fix this issue, if anyone wants to help out or take a look at least. |
@skylord123 one part I don't understand about your feedback though, is that in my experience the session id and token only change when remember-me is being used to authenticate. Do you not store sessions at all in your code, and rely purely on persistent tokens for authentication on every request? If so that should be improved already in 5.3.0 by #40972 which will only refresh the token once a minute. |
@Seldaek Check the
Whereas if you look at the exact same function inside of I store sessions in my code. I am using the default authentication system. I just moved to use If you check out my blog link I posted you will see that all I do to fix this is copy the class and comment out the part of the EDIT: As far as I know the EDIT 2: Shouldn't these two classes function identically except one stores in the DB? If we are going to auto-rotate the remember me token on every request it should be done for both classes not just for persistent storage. |
@skylord123 I can not reproduce this, for me PersistentTokenBasedRememberMeServices::processAutoLoginCookie only gets called if I delete my session cookie, it gets called once while the reauthentication happens and then not anymore on subsequent requests. I'm using Symfony 5.2 and the new Authenticator stuff, not sure if that makes a difference, but IMO the PersistentTokenBasedRememberMeServices should not be called if an active session is present due to https://github.com/symfony/symfony/blob/5.2/src/Symfony/Component/Security/Http/Authenticator/RememberMeAuthenticator.php#L55-L58 |
@Seldaek That is strange. I am running 5.2 using the latest security setup as well. I am for sure logged into my app (deleting the remember me token and I stay logged in). I put a dump on |
@Seldaek So I noticed my firewall had the So maybe this is just broken for lazy sessions? |
@skylord123 ok that is interesting, indeed I have |
…sts doing remember-me re-authentication (Seldaek) This PR was squashed before being merged into the 5.3-dev branch. Discussion ---------- [Security] [RememberMe] Add support for parallel requests doing remember-me re-authentication | Q | A | ------------- | --- | Branch? | 5.x | Bug fix? | yes | New feature? | yes ish <!-- please update src/**/CHANGELOG.md files --> | Deprecations? | no <!-- please update UPGRADE-*.md and src/**/CHANGELOG.md files --> | Tickets | Fix #40971, Fix #28314, Fix #18384 | License | MIT | Doc PR | symfony/symfony-docs#... <!-- required for new features --> This is a possible implementation to gather feedback mostly.. `TokenVerifierInterface` naming is kinda bad perhaps.. But my goal would be to merge it in TokenProviderInterface for 6.0 so it's not so important. Not sure if/how to best indicate this in terms of deprecation notices. Anyway wondering if this would be an acceptable implementation (ideally in an application I would probably override the new methods from DoctrineTokenProvider to something like this which is less of a hack and does expiration properly: ```php public function verifyToken(PersistentTokenInterface $token, string $tokenValue) { if (hash_equals($token->getTokenValue(), $tokenValue)) { return true; } if (!$this->cache->hasItem('rememberme-' . $token->getSeries())) { return false; } /** `@var` CacheItem $item */ $item = $this->cache->getItem('rememberme-' . $token->getSeries()); $oldToken = $item->get(); return hash_equals($oldToken, $tokenValue); } public function updateExistingToken(PersistentTokenInterface $token, string $tokenValue, \DateTimeInterface $lastUsed): void { $this->updateToken($token->getSeries(), $tokenValue, $lastUsed); /** `@var` CacheItem $item */ $item = $this->cache->getItem('rememberme-'.$token->getSeries()); $item->set($token->getTokenValue()); $item->expiresAfter(60); $this->cache->save($item); } ``` If you think it'd be fine to require optionally the cache inside DoctrineTokenProvider to enable this feature instead of the hackish way I did it, that'd be ok for me too. The current `DoctrineTokenProvider` implementation of `TokenVerifierInterface` relies on the lucky fact that series are generated using `base64_encode(random_bytes(64))` which always ends in the `==` padding of base64, so that allowed me to store an alternative token value temporarily by replacing `==` with `_`. Alternative implementation options: 1. Inject cache in `DoctrineTokenProvider` and do a proper implementation (as shown above) that way 2. Do not implement at all in `DoctrineTokenProvider` and let users who care implement this themselves. 3. Implement as a new `token_verifier` option that could be configured on the `firewall->remember_me` key so you can pass an implementation if needed, and possibly ship a default one using cache that could be autoconfigured 4. Add events that allow modifying the token to be verified, and allow receiving the newly updated token incl series, instead of TokenVerifierInterface, but then we need to inject a dispatcher in RememberMeAuthenticator. `@chalasr` `@wouterj` sorry for the long description but in the hope of getting this included in 5.3.0, if you can provide guidance I will happily work on this further tomorrow to try and wrap it up ASAP. Commits ------- 1992337 [Security] [RememberMe] Add support for parallel requests doing remember-me re-authentication
Symfony version(s) affected: 4.1.3
Description
When using "Remember me" functionality, session is regenerated with every request. Due to concurrent requests done by browser session is effectively lost which leads to e.g. CSRF token mismatch.
Authentication is kept, user is still logged in (because of Remember me), but the session is lost.
In
\Symfony\Component\Security\Http\Firewall\RememberMeListener::handle:87
following method is called\Symfony\Component\Security\Http\Session\SessionAuthenticationStrategy::onAuthentication
. With default session strategymigrate
it leads to callingsession_regenerate_id($destroy = true)
in every request. Session ID is changed A->B with main request. For other requests done by browser session ID is B, but changed B->C, B->D etc.How to reproduce
fragment from
config/packages/security.yaml
Possible Solution
none
(probably not safe) orAdditional context
The text was updated successfully, but these errors were encountered: