티스토리 수익 글 보기

티스토리 수익 글 보기

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

Commit 1721035

Browse files
RealOrangeOnenessita
authored andcommitted
[6.0.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 664652f commit 1721035

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
@@ -56,6 +55,7 @@
5655
from django.test.utils import CaptureQueriesContext
5756
from django.utils import timezone, translation
5857
from django.utils.cache import (
58+
cc_delim_re,
5959
get_cache_key,
6060
has_vary_header,
6161
learn_cache_key,
@@ -2206,8 +2206,6 @@ def test_patch_cache_control(self):
22062206
),
22072207
)
22082208

2209-
cc_delim_re = re.compile(r"\s*,\s*")
2210-
22112209
for initial_cc, newheaders, expected_cc in tests:
22122210
with self.subTest(initial_cc=initial_cc, newheaders=newheaders):
22132211
response = HttpResponse()
@@ -2217,6 +2215,31 @@ def test_patch_cache_control(self):
22172215
parts = set(cc_delim_re.split(response.headers["Cache-Control"]))
22182216
self.assertEqual(parts, expected_cc)
22192217

2218+
def test_has_vary_header(self):
2219+
tests = [
2220+
("*", "*", True),
2221+
("Cookie, *", "*", True),
2222+
("Cookie,*", "*", True),
2223+
("Cookie , *", "*", True),
2224+
# Surronding whitespace on values must be stripped independently of
2225+
# the comma delimiter.
2226+
("* ", "*", True),
2227+
(" *", "*", True),
2228+
("Cookie, * ", "*", True),
2229+
(" Cookie", "Cookie", True),
2230+
("Cookie", "*", False),
2231+
("*", "Cookie", False),
2232+
("cookie", "Cookie", True),
2233+
("Cookie", "cookie", True),
2234+
]
2235+
2236+
for header_value, header_query, has_match in tests:
2237+
with self.subTest(header_value=header_value, header_query=header_query):
2238+
response = HttpResponse()
2239+
response.headers["Vary"] = header_value
2240+
2241+
self.assertIs(has_vary_header(response, header_query), has_match)
2242+
22202243

22212244
@override_settings(
22222245
CACHES={
@@ -2520,9 +2543,15 @@ def hello_world_view_patch_vary_headers_asterisk(request, value):
25202543
return response
25212544

25222545

2546+
def hello_world_view_patch_vary_headers_asterisk_space(request, value):
2547+
response = HttpResponse("Hello World %s" % value)
2548+
patch_vary_headers(response, (" * ",))
2549+
return response
2550+
2551+
25232552
def hello_world_view_vary_headers_includes_asterisk(request, value):
25242553
response = HttpResponse("Hello World %s" % value)
2525-
response["Vary"] = "Cookie, *, Pony"
2554+
response["Vary"] = "Cookie, * , Pony"
25262555
return response
25272556

25282557

@@ -2767,6 +2796,7 @@ def view(request, value):
27672796
def test_vary_asterisk_not_cached(self):
27682797
views_with_cache = (
27692798
cache_page(3)(hello_world_view_patch_vary_headers_asterisk),
2799+
cache_page(3)(hello_world_view_patch_vary_headers_asterisk_space),
27702800
cache_page(3)(hello_world_view_vary_headers_includes_asterisk),
27712801
)
27722802
for view in views_with_cache:

0 commit comments

Comments
 (0)