티스토리 수익 글 보기

티스토리 수익 글 보기

[6.1.x] Fixed CVE-2026-48587 — Ignored whitespace padding when check… · django/django@e06958d · GitHub
Skip to content

Commit e06958d

Browse files
RealOrangeOnenessita
authored andcommitted
[6.1.x] Fixed CVE-2026-48587 — Ignored whitespace padding when checking Vary header values.
Thanks to Navid Rezazadeh for the report and Jacob Walls for review. Backport of 42aa0b3 from main.
1 parent b7b23f4 commit e06958d

4 files changed

Lines changed: 62 additions & 6 deletions

File tree

django/utils/cache.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -332,8 +332,8 @@ def has_vary_header(response, header_query):
332332
if not response.has_header("Vary"):
333333
return False
334334
vary_headers = cc_delim_re.split(response.headers["Vary"])
335-
existing_headers = {header.lower() for header in vary_headers}
336-
return header_query.lower() in existing_headers
335+
existing_headers = {header.lower().strip() for header in vary_headers}
336+
return header_query.lower().strip() in existing_headers
337337

338338

339339
def _i18n_cache_key_suffix(request, cache_key):

docs/releases/5.2.15.txt

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,3 +64,16 @@ cache keys, responses to these requests will now :ref:`vary on
6464

6565
This issue has severity "low" according to the :ref:`Django security policy
6666
<severity-levels>`.
67+
68+
CVE-2026-48587: Potential exposure of private data via whitespace padding in ``Vary`` header
69+
============================================================================================
70+
71+
:class:`~django.middleware.cache.UpdateCacheMiddleware` incorrectly cached
72+
responses whose ``Vary`` header values contained leading or trailing
73+
whitespace. Because ``has_vary_header()`` failed to strip that, a ``Vary: *``
74+
header value with surrounding whitespace was not recognized as containing the
75+
wildcard, causing it to be stored and potentially served from the cache when it
76+
should not have been.
77+
78+
This issue has severity "low" according to the :ref:`Django security policy
79+
<severity-levels>`.

docs/releases/6.0.6.txt

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,19 @@ cache keys, responses to these requests will now :ref:`vary on
6666
This issue has severity "low" according to the :ref:`Django security policy
6767
<severity-levels>`.
6868

69+
CVE-2026-48587: Potential exposure of private data via whitespace padding in ``Vary`` header
70+
============================================================================================
71+
72+
:class:`~django.middleware.cache.UpdateCacheMiddleware` incorrectly cached
73+
responses whose ``Vary`` header values contained leading or trailing
74+
whitespace. Because ``has_vary_header()`` failed to strip that, a ``Vary: *``
75+
header value with surrounding whitespace was not recognized as containing the
76+
wildcard, causing it to be stored and potentially served from the cache when it
77+
should not have been.
78+
79+
This issue has severity "low" according to the :ref:`Django security policy
80+
<severity-levels>`.
81+
6982
Bugfixes
7083
========
7184

tests/cache/tests.py

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
import io
55
import os
66
import pickle
7-
import re
87
import shutil
98
import sys
109
import tempfile
@@ -57,6 +56,7 @@
5756
from django.test.utils import CaptureQueriesContext
5857
from django.utils import timezone, translation
5958
from django.utils.cache import (
59+
cc_delim_re,
6060
get_cache_key,
6161
has_vary_header,
6262
learn_cache_key,
@@ -2309,8 +2309,6 @@ def test_patch_cache_control(self):
23092309
),
23102310
)
23112311

2312-
cc_delim_re = re.compile(r"\s*,\s*")
2313-
23142312
for initial_cc, newheaders, expected_cc in tests:
23152313
with self.subTest(initial_cc=initial_cc, newheaders=newheaders):
23162314
response = HttpResponse()
@@ -2320,6 +2318,31 @@ def test_patch_cache_control(self):
23202318
parts = set(cc_delim_re.split(response.headers["Cache-Control"]))
23212319
self.assertEqual(parts, expected_cc)
23222320

2321+
def test_has_vary_header(self):
2322+
tests = [
2323+
("*", "*", True),
2324+
("Cookie, *", "*", True),
2325+
("Cookie,*", "*", True),
2326+
("Cookie , *", "*", True),
2327+
# Surronding whitespace on values must be stripped independently of
2328+
# the comma delimiter.
2329+
("* ", "*", True),
2330+
(" *", "*", True),
2331+
("Cookie, * ", "*", True),
2332+
(" Cookie", "Cookie", True),
2333+
("Cookie", "*", False),
2334+
("*", "Cookie", False),
2335+
("cookie", "Cookie", True),
2336+
("Cookie", "cookie", True),
2337+
]
2338+
2339+
for header_value, header_query, has_match in tests:
2340+
with self.subTest(header_value=header_value, header_query=header_query):
2341+
response = HttpResponse()
2342+
response.headers["Vary"] = header_value
2343+
2344+
self.assertIs(has_vary_header(response, header_query), has_match)
2345+
23232346

23242347
@override_settings(
23252348
CACHES={
@@ -2623,9 +2646,15 @@ def hello_world_view_patch_vary_headers_asterisk(request, value):
26232646
return response
26242647

26252648

2649+
def hello_world_view_patch_vary_headers_asterisk_space(request, value):
2650+
response = HttpResponse("Hello World %s" % value)
2651+
patch_vary_headers(response, (" * ",))
2652+
return response
2653+
2654+
26262655
def hello_world_view_vary_headers_includes_asterisk(request, value):
26272656
response = HttpResponse("Hello World %s" % value)
2628-
response["Vary"] = "Cookie, *, Pony"
2657+
response["Vary"] = "Cookie, * , Pony"
26292658
return response
26302659

26312660

@@ -2870,6 +2899,7 @@ def view(request, value):
28702899
def test_vary_asterisk_not_cached(self):
28712900
views_with_cache = (
28722901
cache_page(3)(hello_world_view_patch_vary_headers_asterisk),
2902+
cache_page(3)(hello_world_view_patch_vary_headers_asterisk_space),
28732903
cache_page(3)(hello_world_view_vary_headers_includes_asterisk),
28742904
)
28752905
for view in views_with_cache:

0 commit comments

Comments
 (0)