+56
-5
lines changedFilter options
+56
-5
lines changed Original file line number Diff line number Diff line change
@@ -31,9 +31,10 @@
31
31
CONTEXT_SEPARATOR = "\x04"
32
32
33
33
# Maximum number of characters that will be parsed from the Accept-Language
34
-
# header to prevent possible denial of service or memory exhaustion attacks.
35
-
# About 10x longer than the longest value shown on MDN’s Accept-Language page.
36
-
ACCEPT_LANGUAGE_HEADER_MAX_LENGTH = 500
34
+
# header or cookie to prevent possible denial of service or memory exhaustion
35
+
# attacks. About 10x longer than the longest value shown on MDN’s
36
+
# Accept-Language page.
37
+
LANGUAGE_CODE_MAX_LENGTH = 500
37
38
38
39
# Format of Accept-Language header values. From RFC 9110 Sections 12.4.2 and
39
40
# 12.5.4, and RFC 5646 Section 2.1.
@@ -497,11 +498,25 @@ def get_supported_language_variant(lang_code, strict=False):
497
498
If `strict` is False (the default), look for a country-specific variant
498
499
when neither the language code nor its generic variant is found.
499
500
501
+
The language code is truncated to a maximum length to avoid potential
502
+
denial of service attacks.
503
+
500
504
lru_cache should have a maxsize to prevent from memory exhaustion attacks,
501
505
as the provided language codes are taken from the HTTP request. See also
502
506
<https://www.djangoproject.com/weblog/2007/oct/26/security-fix/>.
503
507
"""
504
508
if lang_code:
509
+
# Truncate the language code to a maximum length to avoid potential
510
+
# denial of service attacks.
511
+
if len(lang_code) > LANGUAGE_CODE_MAX_LENGTH:
512
+
if (
513
+
not strict
514
+
and (index := lang_code.rfind("-", 0, LANGUAGE_CODE_MAX_LENGTH)) > 0
515
+
):
516
+
# There is a generic variant under the maximum length accepted length.
517
+
lang_code = lang_code[:index]
518
+
else:
519
+
raise ValueError("'lang_code' exceeds the maximum accepted length")
505
520
# If 'zh-hant-tw' is not supported, try special fallback or subsequent
506
521
# language codes i.e. 'zh-hant' and 'zh'.
507
522
possible_lang_codes = [lang_code]
@@ -625,13 +640,13 @@ def parse_accept_lang_header(lang_string):
625
640
functools.lru_cache() to avoid repetitive parsing of common header values.
626
641
"""
627
642
# If the header value doesn't exceed the maximum allowed length, parse it.
628
-
if len(lang_string) <= ACCEPT_LANGUAGE_HEADER_MAX_LENGTH:
643
+
if len(lang_string) <= LANGUAGE_CODE_MAX_LENGTH:
629
644
return _parse_accept_lang_header(lang_string)
630
645
631
646
# If there is at least one comma in the value, parse up to the last comma
632
647
# before the max length, skipping any truncated parts at the end of the
633
648
# header value.
634
-
if (index := lang_string.rfind(",", 0, ACCEPT_LANGUAGE_HEADER_MAX_LENGTH)) > 0:
649
+
if (index := lang_string.rfind(",", 0, LANGUAGE_CODE_MAX_LENGTH)) > 0:
635
650
return _parse_accept_lang_header(lang_string[:index])
636
651
637
652
# Don't attempt to parse if there is only one language-range value which is
Original file line number Diff line number Diff line change
@@ -1155,6 +1155,11 @@ For a complete discussion on the usage of the following see the
1155
1155
``lang_code`` is ``'es-ar'`` and ``'es'`` is in :setting:`LANGUAGES` but
1156
1156
``'es-ar'`` isn't.
1157
1157
1158
+
``lang_code`` has a maximum accepted length of 500 characters. A
1159
+
:exc:`ValueError` is raised if ``lang_code`` exceeds this limit and
1160
+
``strict`` is ``True``, or if there is no generic variant and ``strict``
1161
+
is ``False``.
1162
+
1158
1163
If ``strict`` is ``False`` (the default), a country-specific variant may
1159
1164
be returned when neither the language code nor its generic variant is found.
1160
1165
For example, if only ``'es-co'`` is in :setting:`LANGUAGES`, that's
@@ -1163,6 +1168,11 @@ For a complete discussion on the usage of the following see the
1163
1168
1164
1169
Raises :exc:`LookupError` if nothing is found.
1165
1170
1171
+
.. versionchanged:: 4.2.14
1172
+
1173
+
In older versions, ``lang_code`` values over 500 characters were
1174
+
processed without raising a :exc:`ValueError`.
1175
+
1166
1176
.. function:: to_locale(language)
1167
1177
1168
1178
Turns a language name (en-us) into a locale name (en_US).
Original file line number Diff line number Diff line change
@@ -32,3 +32,18 @@ directory-traversal via certain inputs when calling :meth:`save()
32
32
<django.core.files.storage.Storage.save()>`.
33
33
34
34
Built-in ``Storage`` sub-classes were not affected by this vulnerability.
35
+
36
+
CVE-2024-39614: Potential denial-of-service vulnerability in ``get_supported_language_variant()``
37
+
=================================================================================================
38
+
39
+
:meth:`~django.utils.translation.get_supported_language_variant` was subject to
40
+
a potential denial-of-service attack when used with very long strings
41
+
containing specific characters.
42
+
43
+
To mitigate this vulnerability, the language code provided to
44
+
:meth:`~django.utils.translation.get_supported_language_variant` is now parsed
45
+
up to a maximum length of 500 characters.
46
+
47
+
When the language code is over 500 characters, a :exc:`ValueError` will now be
48
+
raised if ``strict`` is ``True``, or if there is no generic variant and
49
+
``strict`` is ``False``.
Original file line number Diff line number Diff line change
@@ -65,6 +65,7 @@
65
65
translation_file_changed,
66
66
watch_for_translation_changes,
67
67
)
68
+
from django.utils.translation.trans_real import LANGUAGE_CODE_MAX_LENGTH
68
69
69
70
from .forms import CompanyForm, I18nForm, SelectDateForm
70
71
from .models import Company, TestModel
@@ -1888,6 +1889,16 @@ def test_get_supported_language_variant_real(self):
1888
1889
g("xyz")
1889
1890
with self.assertRaises(LookupError):
1890
1891
g("xy-zz")
1892
+
msg = "'lang_code' exceeds the maximum accepted length"
1893
+
with self.assertRaises(LookupError):
1894
+
g("x" * LANGUAGE_CODE_MAX_LENGTH)
1895
+
with self.assertRaisesMessage(ValueError, msg):
1896
+
g("x" * (LANGUAGE_CODE_MAX_LENGTH + 1))
1897
+
# 167 * 3 = 501 which is LANGUAGE_CODE_MAX_LENGTH + 1.
1898
+
self.assertEqual(g("en-" * 167), "en")
1899
+
with self.assertRaisesMessage(ValueError, msg):
1900
+
g("en-" * 167, strict=True)
1901
+
self.assertEqual(g("en-" * 30000), "en") # catastrophic test
1891
1902
1892
1903
def test_get_supported_language_variant_null(self):
1893
1904
g = trans_null.get_supported_language_variant
You can’t perform that action at this time.
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4