티스토리 수익 글 보기

티스토리 수익 글 보기

[6.1.x] Fixed CVE-2026-48588 — Prevented caching of responses that s… · django/django@c2a936a · GitHub
Skip to content

Commit c2a936a

Browse files
nessitajacobtylerwalls
authored andcommitted
[6.1.x] Fixed CVE-2026-48588 — Prevented caching of responses that set cookies and vary on Cookie.
`UpdateCacheMiddleware` skipped caching `Set-Cookie` responses that vary on `Cookie` only when the request had no cookies at all. A request carrying an unrelated cookie bypassed the guard, allowing a newly-issued session cookie to be stored in Django’s shared cache. The guard now applies whenever a response both sets a cookie and varies on Cookie, regardless of what cookies the incoming request carried. Thanks Chris Whyland for the report, Jake Howard for initial triage, and Jacob Walls for reviews. Backport of 6e365f8 from main.
1 parent 2d70687 commit c2a936a

4 files changed

Lines changed: 61 additions & 13 deletions

File tree

django/middleware/cache.py

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -95,12 +95,8 @@ def process_response(self, request, response):
9595
return response
9696

9797
# Don't cache responses that set a user-specific (and maybe security
98-
# sensitive) cookie in response to a cookie-less request.
99-
if (
100-
not request.COOKIES
101-
and response.cookies
102-
and has_vary_header(response, "Cookie")
103-
):
98+
# sensitive) cookie while varying on Cookie.
99+
if response.cookies and has_vary_header(response, "Cookie"):
104100
return response
105101

106102
# Don't cache responses when the Cache-Control header is set to

docs/releases/5.2.16.txt

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,17 @@ Django 5.2.16 release notes
55
*July 7, 2026*
66

77
Django 5.2.16 fixes three security issues with severity "low" in 5.2.15.
8+
9+
CVE-2026-48588: Potential exposure of private data via cached ``Set-Cookie`` response
10+
=====================================================================================
11+
12+
:class:`~django.middleware.cache.UpdateCacheMiddleware` and
13+
:func:`~django.views.decorators.cache.cache_page` avoided caching responses
14+
that set a cookie while varying on ``Cookie`` only when the incoming request
15+
contained no cookies at all. When the request already carried an unrelated
16+
cookie (such as a language or theme preference cookie), the protection did not
17+
apply, allowing a response that sets a session or other sensitive cookie to be
18+
stored in Django's shared cache.
19+
20+
This issue has severity "low" according to the :ref:`Django security policy
21+
<severity-levels>`.

docs/releases/6.0.7.txt

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,20 @@ Django 6.0.7 release notes
77
Django 6.0.7 fixes three security issues with severity "low" and one bug in
88
6.0.6.
99

10+
CVE-2026-48588: Potential exposure of private data via cached ``Set-Cookie`` response
11+
=====================================================================================
12+
13+
:class:`~django.middleware.cache.UpdateCacheMiddleware` and
14+
:func:`~django.views.decorators.cache.cache_page` avoided caching responses
15+
that set a cookie while varying on ``Cookie`` only when the incoming request
16+
contained no cookies at all. When the request already carried an unrelated
17+
cookie (such as a language or theme preference cookie), the protection did not
18+
apply, allowing a response that sets a session or other sensitive cookie to be
19+
stored in Django's shared cache.
20+
21+
This issue has severity "low" according to the :ref:`Django security policy
22+
<severity-levels>`.
23+
1024
Bugfixes
1125
========
1226

tests/cache/tests.py

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3106,18 +3106,42 @@ def test_authorization_header_exception_superstring(self):
31063106

31073107
def test_sensitive_cookie_not_cached(self):
31083108
"""
3109-
Django must prevent caching of responses that set a user-specific (and
3110-
maybe security sensitive) cookie in response to a cookie-less request.
3109+
Responses that set a new cookie not present in the request are not
3110+
cached, regardless of whether the request already had other cookies.
31113111
"""
3112-
request = self.factory.get("/view/")
3113-
csrf_middleware = CsrfViewMiddleware(csrf_view)
3114-
csrf_middleware.process_view(request, csrf_view, (), {})
3115-
cache_middleware = CacheMiddleware(csrf_middleware)
3112+
for headers in ({}, {"HTTP_COOKIE": "unrelated=value"}):
3113+
with self.subTest(headers=headers):
3114+
request = self.factory.get("/view/", **headers)
3115+
csrf_middleware = CsrfViewMiddleware(csrf_view)
3116+
csrf_middleware.process_view(request, csrf_view, (), {})
3117+
cache_middleware = CacheMiddleware(csrf_middleware)
3118+
3119+
self.assertIsNone(cache_middleware.process_request(request))
3120+
cache_middleware(request)
3121+
3122+
# Inserting a CSRF cookie prevented caching.
3123+
self.assertIsNone(cache_middleware.process_request(request))
3124+
3125+
def test_refreshed_cookie_not_cached(self):
3126+
"""
3127+
A response that updates the value of a cookie already present in the
3128+
request is not cached. Caching it would allow a stale cookie value to
3129+
be served to a different client via a Vary: Cookie cache hit.
3130+
"""
3131+
3132+
def refreshing_cookie_view(request):
3133+
response = HttpResponse("content")
3134+
response.set_cookie("session", "new_value")
3135+
patch_vary_headers(response, ("Cookie",))
3136+
return response
3137+
3138+
request = self.factory.get("/view/", HTTP_COOKIE="session=old_value")
3139+
cache_middleware = CacheMiddleware(refreshing_cookie_view)
31163140

31173141
self.assertIsNone(cache_middleware.process_request(request))
31183142
cache_middleware(request)
31193143

3120-
# Inserting a CSRF cookie in a cookie-less request prevented caching.
3144+
# The response refreshed an existing cookie so it must not be cached.
31213145
self.assertIsNone(cache_middleware.process_request(request))
31223146

31233147
def test_304_response_has_http_caching_headers_but_not_cached(self):

0 commit comments

Comments
 (0)