티스토리 수익 글 보기

티스토리 수익 글 보기

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

Commit 721685a

Browse files
nessitajacobtylerwalls
authored andcommitted
[5.2.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 61a829d commit 721685a

3 files changed

Lines changed: 47 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>`.

tests/cache/tests.py

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2824,18 +2824,42 @@ def test_authorization_header_exceptions(self):
28242824

28252825
def test_sensitive_cookie_not_cached(self):
28262826
"""
2827-
Django must prevent caching of responses that set a user-specific (and
2828-
maybe security sensitive) cookie in response to a cookie-less request.
2827+
Responses that set a new cookie not present in the request are not
2828+
cached, regardless of whether the request already had other cookies.
28292829
"""
2830-
request = self.factory.get("/view/")
2831-
csrf_middleware = CsrfViewMiddleware(csrf_view)
2832-
csrf_middleware.process_view(request, csrf_view, (), {})
2833-
cache_middleware = CacheMiddleware(csrf_middleware)
2830+
for headers in ({}, {"HTTP_COOKIE": "unrelated=value"}):
2831+
with self.subTest(headers=headers):
2832+
request = self.factory.get("/view/", **headers)
2833+
csrf_middleware = CsrfViewMiddleware(csrf_view)
2834+
csrf_middleware.process_view(request, csrf_view, (), {})
2835+
cache_middleware = CacheMiddleware(csrf_middleware)
2836+
2837+
self.assertIsNone(cache_middleware.process_request(request))
2838+
cache_middleware(request)
2839+
2840+
# Inserting a CSRF cookie prevented caching.
2841+
self.assertIsNone(cache_middleware.process_request(request))
2842+
2843+
def test_refreshed_cookie_not_cached(self):
2844+
"""
2845+
A response that updates the value of a cookie already present in the
2846+
request is not cached. Caching it would allow a stale cookie value to
2847+
be served to a different client via a Vary: Cookie cache hit.
2848+
"""
2849+
2850+
def refreshing_cookie_view(request):
2851+
response = HttpResponse("content")
2852+
response.set_cookie("session", "new_value")
2853+
patch_vary_headers(response, ("Cookie",))
2854+
return response
2855+
2856+
request = self.factory.get("/view/", HTTP_COOKIE="session=old_value")
2857+
cache_middleware = CacheMiddleware(refreshing_cookie_view)
28342858

28352859
self.assertIsNone(cache_middleware.process_request(request))
28362860
cache_middleware(request)
28372861

2838-
# Inserting a CSRF cookie in a cookie-less request prevented caching.
2862+
# The response refreshed an existing cookie so it must not be cached.
28392863
self.assertIsNone(cache_middleware.process_request(request))
28402864

28412865
def test_304_response_has_http_caching_headers_but_not_cached(self):

0 commit comments

Comments
 (0)