티스토리 수익 글 보기

티스토리 수익 글 보기

[5.2.x] Fixed CVE-2026-8404 — Used Cache-Control directives case-ins… · django/django@366d9ae · GitHub
Skip to content

Commit 366d9ae

Browse files
RealOrangeOnenessita
authored andcommitted
[5.2.x] Fixed CVE-2026-8404 — Used Cache-Control directives case-insensitively in UpdateCacheMiddleware.
Thanks Ahmed Badawe for the report, and Jacob Walls for reviews. This commit includes: [5.2.x] Fixed #36560 — Prevented UpdateCacheMiddleware from caching responses with Cache-Control ‘no-cache’ or ‘no-store’. Backport of ed7c1a5 from main. Backport of d618d7a from main.
1 parent 4e47d2b commit 366d9ae

3 files changed

Lines changed: 46 additions & 12 deletions

File tree

django/middleware/cache.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,8 +100,17 @@ def process_response(self, request, response):
100100
):
101101
return response
102102

103-
# Don't cache a response with 'Cache-Control: private'
104-
if "private" in response.get("Cache-Control", ()):
103+
# Don't cache responses when the Cache-Control header is set to
104+
# private, no-cache, or no-store.
105+
cache_control = response.get("Cache-Control", "").lower()
106+
if cache_control and any(
107+
directive in cache_control
108+
for directive in (
109+
"private",
110+
"no-cache",
111+
"no-store",
112+
)
113+
):
105114
return response
106115

107116
# Don't cache responses when the Vary header contains '*'.

docs/releases/5.2.15.txt

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,19 @@ Connections configured with :setting:`EMAIL_USE_SSL` are not affected.
3535

3636
This issue has severity "low" according to the :ref:`Django security policy
3737
<severity-levels>`.
38+
39+
CVE-2026-8404: Potential exposure of private data via case-sensitive ``Cache-Control`` directives
40+
=================================================================================================
41+
42+
:class:`~django.middleware.cache.UpdateCacheMiddleware` and
43+
:func:`~django.views.decorators.cache.cache_page` incorrectly cached responses
44+
marked with private ``Cache-Control`` directives when using mixed or uppercase
45+
values (e.g. ``Private``).
46+
47+
The :func:`~django.views.decorators.cache.cache_control` decorator and
48+
:func:`~django.utils.cache.patch_cache_control` function were not affected,
49+
since they normalize directives to lowercase. This issue only affects responses
50+
where ``Cache-Control`` is set manually.
51+
52+
This issue has severity "low" according to the :ref:`Django security policy
53+
<severity-levels>`.

tests/cache/tests.py

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2734,16 +2734,25 @@ def test_cache_page_timeout(self):
27342734
)
27352735
cache.clear()
27362736

2737-
def test_cached_control_private_not_cached(self):
2738-
"""Responses with 'Cache-Control: private' are not cached."""
2739-
view_with_private_cache = cache_page(3)(
2740-
cache_control(private=True)(hello_world_view)
2741-
)
2742-
request = self.factory.get("/view/")
2743-
response = view_with_private_cache(request, "1")
2744-
self.assertEqual(response.content, b"Hello World 1")
2745-
response = view_with_private_cache(request, "2")
2746-
self.assertEqual(response.content, b"Hello World 2")
2737+
def test_cache_control_not_cached(self):
2738+
"""
2739+
Responses with 'Cache-Control: private/no-cache/no-store' are
2740+
not cached.
2741+
"""
2742+
for cc in ("private", "no-cache", "no-store", "PRIVATE", "NO-store"):
2743+
with self.subTest(cache_control=cc):
2744+
# Cannot use @cache_control() as it lowercases directives.
2745+
@cache_page(3)
2746+
def view(request, value):
2747+
return HttpResponse(
2748+
f"Hello World {value}", headers={"Cache-Control": cc}
2749+
)
2750+
2751+
request = self.factory.get("/view/")
2752+
response = view(request, "1")
2753+
self.assertEqual(response.content, b"Hello World 1")
2754+
response = view(request, "2")
2755+
self.assertEqual(response.content, b"Hello World 2")
27472756

27482757
def test_vary_asterisk_not_cached(self):
27492758
views_with_cache = (

0 commit comments

Comments
 (0)