Repository navigation
fix(session): Prevent stale requests from replacing a regenerated session - #65928
Open
DerDreschner wants to merge 1 commit into
Open
DerDreschner wants to merge 1 commit into
DerDreschner wants to merge 1 commit into
Conversation
DerDreschner
requested review from
provokateurin,
salmart-dev and
sorbaugh
and
a balanced review from Copilot
and removed request for
a team
October 26, 2026 13:45
Contributor
Author
|
/backport to stable35 |
Contributor
Author
|
/backport to stable34 |
Contributor
Author
|
/backport to stable33 |
DerDreschner
force-pushed
the
fix/stale-session-ids
branch
from
October 26, 2026 14:24
82b49e0 to
cf9752b
Compare
…sion Regenerating the session id - on login, on unlocking a public share, on a remember-me login - leaves the requests that are already on their way with the previous session cookie. PHP starts a session for that id again and sends it back in a Set-Cookie header, so the id the browser had just stored is overwritten and everything the regenerating request did is lost. For a logged-in user who unlocks a password-protected public share this shows up as a 401 on PROPFIND /public.php/dav/files/<token>/. Mark the old session as superseded instead of deleting it, and move the requests that still arrive on it to an empty session. Within a grace period of 60 seconds they get no session cookie at all, so they can no longer replace the regenerated id the browser holds. reopen() does the same and only sends a cookie when the id it was given is gone for good. Assisted-by: ClaudeCode:claude-opus-5 Signed-off-by: David Dreschner <david.dreschner@nextcloud.com>
DerDreschner
force-pushed
the
fix/stale-session-ids
branch
from
October 26, 2026 14:55
cf9752b to
d94241c
Compare
Comment on lines
+139
to
142
| if (!$this->switchToNewId($oldId, $deleteOldSession)) { | ||
| $this->logger->warning('Could not regenerate the session id'); | ||
| return; | ||
| } |
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.


Summary
When the session id is being regenerated (for example when you unlock a public share while a request from another tab is already on its way), the session can flap back to the old one due to stale requests. When PHP receives a stale request with the previous session id, it starts a new session with the old session id and sets a new cookie with the old session id. The browser is pushed back onto the old session and loses everything the regenerating request did, which leads to intermittent 401 errors - for a password-protected public share, the file list fails with a 401 on PROPFIND
/public.php/dav/files/<token>/.To fix the issue, I no longer delete the old session: it keeps a marker that its id was replaced. A request that still arrives on that id gets an empty session and, for a grace period of 60 seconds, no session cookie at all, so it can no longer replace the id the browser just stored. After the grace period it gets a fresh session with a cookie as usual.
Internal::reopen()now starts the session without cookies for the same reason - until now it re-emitted the cookie on every late session write.Checklist
3. to review, feature component)stable32)AI (if applicable)