티스토리 수익 글 보기

티스토리 수익 글 보기

[6.0.x] Fixed CVE-2026-35192 — Ensured Vary header is sent when sett… · django/django@1b0184a · GitHub
Skip to content

Commit 1b0184a

Browse files
RealOrangeOnesarahboyce
authored andcommitted
[6.0.x] Fixed CVE-2026-35192 — Ensured Vary header is sent when setting session cookie with SESSION_SAVE_EVERY_REQUEST=True.
Thank you Jacob Walls and Natalia Bidart for reviews. Backport of 7f6e9b5 from main.
1 parent ad8f9e1 commit 1b0184a

4 files changed

Lines changed: 58 additions & 3 deletions

File tree

django/contrib/sessions/middleware.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,11 @@ def process_response(self, request, response):
4040
domain=settings.SESSION_COOKIE_DOMAIN,
4141
samesite=settings.SESSION_COOKIE_SAMESITE,
4242
)
43-
patch_vary_headers(response, ("Cookie",))
43+
need_vary_cookie = True
4444
else:
45-
if accessed:
46-
patch_vary_headers(response, ("Cookie",))
45+
# If the session was accessed, it must be varied on, regardless of
46+
# whether it was modified or will be saved.
47+
need_vary_cookie = accessed
4748
if (modified or settings.SESSION_SAVE_EVERY_REQUEST) and not empty:
4849
if request.session.get_expire_at_browser_close():
4950
max_age = None
@@ -74,4 +75,8 @@ def process_response(self, request, response):
7475
httponly=settings.SESSION_COOKIE_HTTPONLY or None,
7576
samesite=settings.SESSION_COOKIE_SAMESITE,
7677
)
78+
# With a session cookie set, it must be varied on.
79+
need_vary_cookie = True
80+
if need_vary_cookie:
81+
patch_vary_headers(response, ("Cookie",))
7782
return response

docs/releases/5.2.14.txt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,14 @@ relying on :setting:`FILE_UPLOAD_MAX_MEMORY_SIZE`.
2020

2121
This issue has severity "low" according to the :ref:`Django security policy
2222
<security-disclosure>`.
23+
24+
CVE-2026-35192: Session fixation via public cached pages and ``SESSION_SAVE_EVERY_REQUEST``
25+
===========================================================================================
26+
27+
Response headers did not :ref:`vary on <using-vary-headers>` cookies if a
28+
session was not modified, but :setting:`SESSION_SAVE_EVERY_REQUEST` was
29+
``True``. A remote attacker could steal a user's session after that user visits
30+
a cached public page.
31+
32+
This issue has severity "low" according to the :ref:`Django security policy
33+
<security-disclosure>`.

docs/releases/6.0.5.txt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,17 @@ relying on :setting:`FILE_UPLOAD_MAX_MEMORY_SIZE`.
2121
This issue has severity "low" according to the :ref:`Django security policy
2222
<security-disclosure>`.
2323

24+
CVE-2026-35192: Session fixation via public cached pages and ``SESSION_SAVE_EVERY_REQUEST``
25+
===========================================================================================
26+
27+
Response headers did not :ref:`vary on <using-vary-headers>` cookies if a
28+
session was not modified, but :setting:`SESSION_SAVE_EVERY_REQUEST` was
29+
``True``. A remote attacker could steal a user's session after that user visits
30+
a cached public page.
31+
32+
This issue has severity "low" according to the :ref:`Django security policy
33+
<security-disclosure>`.
34+
2435
Bugfixes
2536
========
2637

tests/sessions_tests/tests.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1022,6 +1022,7 @@ def test_secure_session_cookie(self):
10221022
# Handle the response through the middleware
10231023
response = middleware(request)
10241024
self.assertIs(response.cookies[settings.SESSION_COOKIE_NAME]["secure"], True)
1025+
self.assertEqual(response.headers["Vary"], "Cookie")
10251026

10261027
@override_settings(SESSION_COOKIE_HTTPONLY=True)
10271028
def test_httponly_session_cookie(self):
@@ -1162,6 +1163,7 @@ def response_ending_session(request):
11621163
),
11631164
str(response.cookies[settings.SESSION_COOKIE_NAME]),
11641165
)
1166+
self.assertEqual(response.headers["Vary"], "Cookie")
11651167

11661168
def test_flush_empty_without_session_cookie_doesnt_set_cookie(self):
11671169
def response_ending_session(request):
@@ -1179,6 +1181,32 @@ def response_ending_session(request):
11791181
# The session is accessed so "Vary: Cookie" should be set.
11801182
self.assertEqual(response.headers["Vary"], "Cookie")
11811183

1184+
@override_settings(SESSION_SAVE_EVERY_REQUEST=True)
1185+
def test_save_every_request_with_non_empty_session_renews_session_cookie(self):
1186+
request = self.request_factory.get("/")
1187+
middleware = SessionMiddleware(self.get_response_touching_session)
1188+
1189+
# Make sure the request has a session.
1190+
middleware(request)
1191+
1192+
# A cookie should be set.
1193+
self.assertIs(request.session.is_empty(), False)
1194+
self.assertEqual(request.session["hello"], "world")
1195+
1196+
request.COOKIES[settings.SESSION_COOKIE_NAME] = request.session.session_key
1197+
1198+
def simple_view(request):
1199+
return HttpResponse("Session test")
1200+
1201+
middleware = SessionMiddleware(simple_view)
1202+
response = middleware(request)
1203+
1204+
# A cookie should be set because SESSION_SAVE_EVERY_REQUEST=True,
1205+
# even though the session wasn't touched.
1206+
self.assertIn(settings.SESSION_COOKIE_NAME, response.cookies)
1207+
# There's a session, so also Vary on it.
1208+
self.assertEqual(response.headers["Vary"], "Cookie")
1209+
11821210
def test_empty_session_saved(self):
11831211
"""
11841212
If a session is emptied of data but still has a key, it should still

0 commit comments

Comments
 (0)