summaryrefslogtreecommitdiff
path: root/django
diff options
context:
space:
mode:
authorNick Pope <nick@nickpope.me.uk>2021-02-16 10:14:17 +0000
committerCarlton Gibson <carlton.gibson@noumenal.es>2021-02-19 09:15:09 +0100
commitbe8237c7cce24b06aabde0b97afce98ddabbe3b6 (patch)
treeb4a01e3e621eaf21fe64dc33c081b94c37ca1600 /django
parent0debc6ba5b99027dccd287f8c247b328e4fe9483 (diff)
[3.2.x] Fixed CVE-2021-23336 -- Fixed web cache poisoning via django.utils.http.parse_qsl().
Diffstat (limited to 'django')
-rw-r--r--django/http/request.py5
-rw-r--r--django/utils/http.py16
2 files changed, 15 insertions, 6 deletions
diff --git a/django/http/request.py b/django/http/request.py
index 2488bf9ccd..195341ec4b 100644
--- a/django/http/request.py
+++ b/django/http/request.py
@@ -29,7 +29,10 @@ from .multipartparser import parse_header
# detect whether the max_num_fields argument is available as this security fix
# was backported to Python 3.6.8 and 3.7.2, and may also have been applied by
# downstream package maintainers to other versions in their repositories.
-if not func_supports_parameter(parse_qsl, 'max_num_fields'):
+if (
+ not func_supports_parameter(parse_qsl, 'max_num_fields') or
+ not func_supports_parameter(parse_qsl, 'separator')
+):
from django.utils.http import parse_qsl
diff --git a/django/utils/http.py b/django/utils/http.py
index 810d7970ba..61db5e6028 100644
--- a/django/utils/http.py
+++ b/django/utils/http.py
@@ -415,13 +415,13 @@ def _url_has_allowed_host_and_scheme(url, allowed_hosts, require_https=False):
# TODO: Remove when dropping support for PY37.
def parse_qsl(
qs, keep_blank_values=False, strict_parsing=False, encoding='utf-8',
- errors='replace', max_num_fields=None,
+ errors='replace', max_num_fields=None, separator='&',
):
"""
Return a list of key/value tuples parsed from query string.
- Backport of urllib.parse.parse_qsl() from Python 3.8.
- Copyright (C) 2020 Python Software Foundation (see LICENSE.python).
+ Backport of urllib.parse.parse_qsl() from Python 3.8.8.
+ Copyright (C) 2021 Python Software Foundation (see LICENSE.python).
----
@@ -447,19 +447,25 @@ def parse_qsl(
max_num_fields: int. If set, then throws a ValueError if there are more
than n fields read by parse_qsl().
+ separator: str. The symbol to use for separating the query arguments.
+ Defaults to &.
+
Returns a list, as G-d intended.
"""
qs, _coerce_result = _coerce_args(qs)
+ if not separator or not isinstance(separator, (str, bytes)):
+ raise ValueError('Separator must be of type string or bytes.')
+
# If max_num_fields is defined then check that the number of fields is less
# than max_num_fields. This prevents a memory exhaustion DOS attack via
# post bodies with many fields.
if max_num_fields is not None:
- num_fields = 1 + qs.count('&') + qs.count(';')
+ num_fields = 1 + qs.count(separator)
if max_num_fields < num_fields:
raise ValueError('Max number of fields exceeded')
- pairs = [s2 for s1 in qs.split('&') for s2 in s1.split(';')]
+ pairs = [s1 for s1 in qs.split(separator)]
r = []
for name_value in pairs:
if not name_value and not strict_parsing: