1
0
mirror of https://github.com/django/django.git synced 2024-12-22 09:05:43 +00:00

Fixed CVE-2023-23969 -- Prevented DoS with pathological values for Accept-Language.

The parsed values of Accept-Language headers are cached in order to
avoid repetitive parsing. This leads to a potential denial-of-service
vector via excessive memory usage if the raw value of Accept-Language
headers is very large.

Accept-Language headers are now limited to a maximum length in order
to avoid this issue.
This commit is contained in:
Nick Pope 2023-01-25 12:21:48 +01:00 committed by Mariusz Felisiak
parent 110b3b8356
commit 8c660fb592
5 changed files with 72 additions and 5 deletions

View File

@ -30,6 +30,11 @@ _default = None
# magic gettext number to separate context from message
CONTEXT_SEPARATOR = "\x04"
# Maximum number of characters that will be parsed from the Accept-Language
# header to prevent possible denial of service or memory exhaustion attacks.
# About 10x longer than the longest value shown on MDNs Accept-Language page.
ACCEPT_LANGUAGE_HEADER_MAX_LENGTH = 500
# Format of Accept-Language header values. From RFC 9110 Sections 12.4.2 and
# 12.5.4, and RFC 5646 Section 2.1.
accept_language_re = _lazy_re_compile(
@ -582,7 +587,7 @@ def get_language_from_request(request, check_path=False):
@functools.lru_cache(maxsize=1000)
def parse_accept_lang_header(lang_string):
def _parse_accept_lang_header(lang_string):
"""
Parse the lang_string, which is the body of an HTTP Accept-Language
header, and return a tuple of (lang, q-value), ordered by 'q' values.
@ -604,3 +609,27 @@ def parse_accept_lang_header(lang_string):
result.append((lang, priority))
result.sort(key=lambda k: k[1], reverse=True)
return tuple(result)
def parse_accept_lang_header(lang_string):
"""
Parse the value of the Accept-Language header up to a maximum length.
The value of the header is truncated to a maximum length to avoid potential
denial of service and memory exhaustion attacks. Excessive memory could be
used if the raw value is very large as it would be cached due to the use of
functools.lru_cache() to avoid repetitive parsing of common header values.
"""
# If the header value doesn't exceed the maximum allowed length, parse it.
if len(lang_string) <= ACCEPT_LANGUAGE_HEADER_MAX_LENGTH:
return _parse_accept_lang_header(lang_string)
# If there is at least one comma in the value, parse up to the last comma
# before the max length, skipping any truncated parts at the end of the
# header value.
if (index := lang_string.rfind(",", 0, ACCEPT_LANGUAGE_HEADER_MAX_LENGTH)) > 0:
return _parse_accept_lang_header(lang_string[:index])
# Don't attempt to parse if there is only one language-range value which is
# longer than the maximum allowed length and so truncated.
return ()

View File

@ -6,4 +6,12 @@ Django 3.2.17 release notes
Django 3.2.17 fixes a security issue with severity "moderate" in 3.2.16.
...
CVE-2023-23969: Potential denial-of-service via ``Accept-Language`` headers
===========================================================================
The parsed values of ``Accept-Language`` headers are cached in order to avoid
repetitive parsing. This leads to a potential denial-of-service vector via
excessive memory usage if large header values are sent.
In order to avoid this vulnerability, the ``Accept-Language`` header is now
parsed up to a maximum length.

View File

@ -6,4 +6,12 @@ Django 4.0.9 release notes
Django 4.0.9 fixes a security issue with severity "moderate" in 4.0.8.
...
CVE-2023-23969: Potential denial-of-service via ``Accept-Language`` headers
===========================================================================
The parsed values of ``Accept-Language`` headers are cached in order to avoid
repetitive parsing. This leads to a potential denial-of-service vector via
excessive memory usage if large header values are sent.
In order to avoid this vulnerability, the ``Accept-Language`` header is now
parsed up to a maximum length.

View File

@ -4,8 +4,18 @@ Django 4.1.6 release notes
*February 1, 2023*
Django 4.1.6 fixes a security issue with severity "moderate" and several bugs
in 4.1.5.
Django 4.1.6 fixes a security issue with severity "moderate" and a bug in
4.1.5.
CVE-2023-23969: Potential denial-of-service via ``Accept-Language`` headers
===========================================================================
The parsed values of ``Accept-Language`` headers are cached in order to avoid
repetitive parsing. This leads to a potential denial-of-service vector via
excessive memory usage if large header values are sent.
In order to avoid this vulnerability, the ``Accept-Language`` header is now
parsed up to a maximum length.
Bugfixes
========

View File

@ -1501,6 +1501,14 @@ class MiscTests(SimpleTestCase):
("de;q=0.", [("de", 0.0)]),
("en; q=1,", [("en", 1.0)]),
("en; q=1.0, * ; q=0.5", [("en", 1.0), ("*", 0.5)]),
(
"en" + "-x" * 20,
[("en-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x", 1.0)],
),
(
", ".join(["en; q=1.0"] * 20),
[("en", 1.0)] * 20,
),
# Bad headers
("en-gb;q=1.0000", []),
("en;q=0.1234", []),
@ -1517,6 +1525,10 @@ class MiscTests(SimpleTestCase):
("", []),
("en;q=1e0", []),
("en-au;q=.", []),
# Invalid as language-range value too long.
("xxxxxxxx" + "-xxxxxxxx" * 500, []),
# Header value too long, only parse up to limit.
(", ".join(["en; q=1.0"] * 500), [("en", 1.0)] * 45),
]
for value, expected in tests:
with self.subTest(value=value):