﻿id	summary	reporter	owner	description	type	status	component	version	severity	resolution	keywords	cc	stage	has_patch	needs_docs	needs_tests	needs_better_patch	easy	ui_ux
37249	Race condition can undo logout with cache/file session backends	Shai Berger		"Django’s cache and file session backends can resurrect a session that was deleted by logout under a narrow but concrete concurrent-request race.

The vulnerable interleaving occurs when an authenticated same-session request has already reached {{{SessionMiddleware.process_response()}}} and the backend {{{save()}}} path has checked that the old session still exists. If {{{/logout/}}} deletes that same session before the backend performs its final write, the cache/file backend can write the old session key again. {{{SessionMiddleware}}} then raises no {{{SessionInterrupted}}} and sends {{{Set-Cookie}}} for the old {{{sessionid}}}.

This can restore authenticated session data after logout if the late response is applied as the browser’s final cookie state.

The ""signed_cookie"", ""db"" and ""cached_db"" session backends are not affected.

==== Root Cause

The DB session backend updates an existing row using {{{force_update=True}}}. If logout deletes the row before a late save, the update fails and Django
raises {{{UpdateError}}}, which {{{SessionMiddleware}}} converts to {{{SessionInterrupted}}}.

The cache and file backends use a check-then-write pattern instead:

- Cache backend checks existence with {{{cache.get()}}} and then writes with {{{cache.set()}}}.
- File backend checks that the target file exists, then writes a temporary file and moves it into the target path.

Those final writes are not atomically tied to the existence check. If logout deletes the same session after the check but before the final write,
the old key/path is recreated instead of failing.

==== Failure Scenario

A user has an authenticated session, and two same-session requests are in flight concurrently. This can occur through multiple tabs, background requests, delayed form submissions, beacons/fetches, or application endpoints that modify session state around the same time as logout.

The problematic interleaving is:

1. Request A modifies the session and reaches {{{SessionMiddleware.process_response()}}}.
2. Request A’s backend {{{save()}}} path confirms the old session still exists.
3. Request B calls {{{/logout/}}} and deletes the same session.
4. Request A performs its final backend write and recreates the old session key.
5. Request A’s late response sends {{{Set-Cookie: sessionid=<old key>}}}.
6. If the browser applies Request A’s response after logout, a later {{{/whoami/}}} request is authenticated again.

This requires a concurrent same-session request and the narrow save-window interleaving.

==== Important Non-Claim

The simpler flow below was tested and is not sufficient:

1. /slow-save/ pauses inside the view.
2. /logout/ deletes the session.
3. /slow-save/ later finalizes.

In that ordering, cache/file/db/cached_db correctly raise SessionInterrupted
or otherwise do not restore authentication.

==== Credit

This, including the attached test script, was reported to the Security Team by Jaeyoung Jang (@BORAMAE). In the ticket, some edits were made to the original report.

"	Bug	new	contrib.sessions	dev	Normal				Unreviewed	0	0	0	0	0	0
