[Django] #37249: Race condition can undo logout with cache/file session backends

7 views
Skip to first unread message

Django

unread,
Aug 3, 2026, 5:23:16 PMAug 3
to django-...@googlegroups.com
#37249: Race condition can undo logout with cache/file session backends
-----------------------------+--------------------------------------------
Reporter: Shai Berger | Type: Bug
Status: new | Component: contrib.sessions
Version: dev | Severity: Normal
Keywords: | Triage Stage: Unreviewed
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-----------------------------+--------------------------------------------
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.
--
Ticket URL: <https://code.djangoproject.com/ticket/37249>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.

Django

unread,
Aug 3, 2026, 5:23:43 PMAug 3
to django-...@googlegroups.com
#37249: Race condition can undo logout with cache/file session backends
----------------------------------+--------------------------------------
Reporter: Shai Berger | Owner: (none)
Type: Bug | Status: new
Component: contrib.sessions | Version: dev
Severity: Normal | Resolution:
Keywords: | Triage Stage: Unreviewed
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
----------------------------------+--------------------------------------
Changes (by Shai Berger):

* Attachment "session_resurrection_race_repro.py" added.

Django

unread,
Aug 3, 2026, 5:32:46 PMAug 3
to django-...@googlegroups.com
#37249: Race condition can undo logout with cache/file session backends
----------------------------------+--------------------------------------
Reporter: Shai Berger | Owner: (none)
Type: Bug | Status: new
Component: contrib.sessions | Version: dev
Severity: Normal | Resolution:
Keywords: | Triage Stage: Unreviewed
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
----------------------------------+--------------------------------------
Comment (by Shai Berger):

The attached reproduction script builds a minimal Django app with real
auth/session middleware and three endpoints:

- {{{/slow-save/}}}: requires authentication, reads and modifies
{{{request.session}}}.
- {{{/logout/}}}: calls {{{django.contrib.auth.logout(request)}}}.
- {{{/whoami/}}}: returns authentication state and current session key.

For the positive cache/file tests, the script deterministically pauses the
backend between the existence check and final write, calls {{{/logout/}}},
then releases the late save.

Run command:

{{{#!bash
cd /path/to/file
python session_resurrection_race_repro.py
}}}
Expected result:
{{{
logout_deleted_cookie: true
exists_after_logout: false
exists_after_slow: true
slow_set_cookie_same_old_key: true
/whoami/ returns authenticated: true
}}}
--
Ticket URL: <https://code.djangoproject.com/ticket/37249#comment:1>

Django

unread,
Aug 3, 2026, 7:33:22 PMAug 3
to django-...@googlegroups.com
#37249: Race condition can undo logout with cache/file session backends
----------------------------------+------------------------------------
Reporter: Shai Berger | Owner: (none)
Type: Bug | Status: new
Component: contrib.sessions | Version: dev
Severity: Normal | Resolution:
Keywords: not-security | Triage Stage: Accepted
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
----------------------------------+------------------------------------
Changes (by Natalia Bidart):

* keywords: => not-security
* stage: Unreviewed => Accepted

--
Ticket URL: <https://code.djangoproject.com/ticket/37249#comment:2>

Django

unread,
Aug 3, 2026, 11:05:55 PMAug 3
to django-...@googlegroups.com
#37249: Race condition can undo logout with cache/file session backends
----------------------------------+------------------------------------
Reporter: Shai Berger | Owner: (none)
Type: Bug | Status: new
Component: contrib.sessions | Version: dev
Severity: Normal | Resolution:
Keywords: not-security | Triage Stage: Accepted
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
----------------------------------+------------------------------------
Comment (by Jacob Walls):

Solving this in the file backend is probably doable with locks. I read the
discussion in the patches for #8616, and there was a lot of discussion
about avoiding long-held-open locks, so we would need to check this
carefully.

Solving this in the cache backends would require new cache API IMO like
`replace()` to take advantage of locking in redis or memcached.

There is also the `asave()` method on the cache interface to think about
for races.
--
Ticket URL: <https://code.djangoproject.com/ticket/37249#comment:3>

Django

unread,
Aug 5, 2026, 9:30:02 PMAug 5
to django-...@googlegroups.com
#37249: Race condition can undo logout with cache/file session backends
----------------------------------+------------------------------------
Reporter: Shai Berger | Owner: Vishy
Type: Bug | Status: assigned
Component: contrib.sessions | Version: dev
Severity: Normal | Resolution:
Keywords: not-security | Triage Stage: Accepted
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
----------------------------------+------------------------------------
Changes (by Vishy):

* owner: (none) => Vishy
* status: new => assigned

--
Ticket URL: <https://code.djangoproject.com/ticket/37249#comment:4>

Django

unread,
Aug 21, 2026, 11:37:22 PM (2 days ago) Aug 21
to django-...@googlegroups.com
#37249: Race condition can undo logout with cache/file session backends
----------------------------------+------------------------------------
Reporter: Shai Berger | Owner: Vishy
Type: Bug | Status: assigned
Component: contrib.sessions | Version: dev
Severity: Normal | Resolution:
Keywords: not-security | Triage Stage: Accepted
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
----------------------------------+------------------------------------
Changes (by Vishy):

* owner: Vishy => Vishy

--
Ticket URL: <https://code.djangoproject.com/ticket/37249#comment:5>
Reply all
Reply to author
Forward
0 new messages