summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMariusz Felisiak <felisiak.mariusz@gmail.com>2021-05-04 20:50:12 +0200
committerMariusz Felisiak <felisiak.mariusz@gmail.com>2021-05-06 08:48:22 +0200
commit2d2c1d0c97832860fbd6597977e2aae17dd7e5b2 (patch)
treeb40ad19706a34770870288fd792f07b38c8a9f71
parenta937d7f2142eb6f987679efc82f2c74f47d17ce1 (diff)
downloaddjango-2d2c1d0c97832860fbd6597977e2aae17dd7e5b2.tar.gz
[3.2.x] Fixed #32713, Fixed CVE-2021-32052 -- Prevented newlines and tabs from being accepted in URLValidator on Python 3.9.5+.
In Python 3.9.5+ urllib.parse() automatically removes ASCII newlines and tabs from URLs [1, 2]. Unfortunately it created an issue in the URLValidator. URLValidator uses urllib.urlsplit() and urllib.urlunsplit() for creating a URL variant with Punycode which no longer contains newlines and tabs in Python 3.9.5+. As a consequence, the regular expression matched the URL (without unsafe characters) and the source value (with unsafe characters) was considered valid. [1] https://bugs.python.org/issue43882 and [2] https://github.com/python/cpython/commit/76cd81d60310d65d01f9d7b48a8985d8ab89c8b4 Backport of e1e81aa1c4427411e3c68facdd761229ffea6f6f from main.
-rw-r--r--django/core/validators.py3
-rw-r--r--docs/releases/2.2.22.txt22
-rw-r--r--docs/releases/3.1.10.txt22
-rw-r--r--docs/releases/3.2.2.txt19
-rw-r--r--docs/releases/index.txt2
-rw-r--r--tests/validators/tests.py8
6 files changed, 73 insertions, 3 deletions
diff --git a/django/core/validators.py b/django/core/validators.py
index a385819510..f9abec602c 100644
--- a/django/core/validators.py
+++ b/django/core/validators.py
@@ -92,6 +92,7 @@ class URLValidator(RegexValidator):
r'\Z', re.IGNORECASE)
message = _('Enter a valid URL.')
schemes = ['http', 'https', 'ftp', 'ftps']
+ unsafe_chars = frozenset('\t\r\n')
def __init__(self, schemes=None, **kwargs):
super().__init__(**kwargs)
@@ -101,6 +102,8 @@ class URLValidator(RegexValidator):
def __call__(self, value):
if not isinstance(value, str):
raise ValidationError(self.message, code=self.code, params={'value': value})
+ if self.unsafe_chars.intersection(value):
+ raise ValidationError(self.message, code=self.code, params={'value': value})
# Check if the scheme is valid.
scheme = value.split('://')[0].lower()
if scheme not in self.schemes:
diff --git a/docs/releases/2.2.22.txt b/docs/releases/2.2.22.txt
new file mode 100644
index 0000000000..6808a267af
--- /dev/null
+++ b/docs/releases/2.2.22.txt
@@ -0,0 +1,22 @@
+===========================
+Django 2.2.22 release notes
+===========================
+
+*May 6, 2021*
+
+Django 2.2.22 fixes a security issue in 2.2.21.
+
+CVE-2021-32052: Header injection possibility since ``URLValidator`` accepted newlines in input on Python 3.9.5+
+===============================================================================================================
+
+On Python 3.9.5+, :class:`~django.core.validators.URLValidator` didn't prohibit
+newlines and tabs. If you used values with newlines in HTTP response, you could
+suffer from header injection attacks. Django itself wasn't vulnerable because
+:class:`~django.http.HttpResponse` prohibits newlines in HTTP headers.
+
+Moreover, the ``URLField`` form field which uses ``URLValidator`` silently
+removes newlines and tabs on Python 3.9.5+, so the possibility of newlines
+entering your data only existed if you are using this validator outside of the
+form fields.
+
+This issue was introduced by the :bpo:`43882` fix.
diff --git a/docs/releases/3.1.10.txt b/docs/releases/3.1.10.txt
new file mode 100644
index 0000000000..e9a8fcc2d8
--- /dev/null
+++ b/docs/releases/3.1.10.txt
@@ -0,0 +1,22 @@
+===========================
+Django 3.1.10 release notes
+===========================
+
+*May 6, 2021*
+
+Django 3.1.10 fixes a security issue in 3.1.9.
+
+CVE-2021-32052: Header injection possibility since ``URLValidator`` accepted newlines in input on Python 3.9.5+
+===============================================================================================================
+
+On Python 3.9.5+, :class:`~django.core.validators.URLValidator` didn't prohibit
+newlines and tabs. If you used values with newlines in HTTP response, you could
+suffer from header injection attacks. Django itself wasn't vulnerable because
+:class:`~django.http.HttpResponse` prohibits newlines in HTTP headers.
+
+Moreover, the ``URLField`` form field which uses ``URLValidator`` silently
+removes newlines and tabs on Python 3.9.5+, so the possibility of newlines
+entering your data only existed if you are using this validator outside of the
+form fields.
+
+This issue was introduced by the :bpo:`43882` fix.
diff --git a/docs/releases/3.2.2.txt b/docs/releases/3.2.2.txt
index d47da08d6c..a899bc6e29 100644
--- a/docs/releases/3.2.2.txt
+++ b/docs/releases/3.2.2.txt
@@ -2,9 +2,24 @@
Django 3.2.2 release notes
==========================
-*Expected June 1, 2021*
+*May 6, 2021*
-Django 3.2.2 fixes several bugs in 3.2.1.
+Django 3.2.2 fixes a security issue and a bug in 3.2.1.
+
+CVE-2021-32052: Header injection possibility since ``URLValidator`` accepted newlines in input on Python 3.9.5+
+===============================================================================================================
+
+On Python 3.9.5+, :class:`~django.core.validators.URLValidator` didn't prohibit
+newlines and tabs. If you used values with newlines in HTTP response, you could
+suffer from header injection attacks. Django itself wasn't vulnerable because
+:class:`~django.http.HttpResponse` prohibits newlines in HTTP headers.
+
+Moreover, the ``URLField`` form field which uses ``URLValidator`` silently
+removes newlines and tabs on Python 3.9.5+, so the possibility of newlines
+entering your data only existed if you are using this validator outside of the
+form fields.
+
+This issue was introduced by the :bpo:`43882` fix.
Bugfixes
========
diff --git a/docs/releases/index.txt b/docs/releases/index.txt
index 62e21aa6ca..86e50dd3ff 100644
--- a/docs/releases/index.txt
+++ b/docs/releases/index.txt
@@ -34,6 +34,7 @@ versions of the documentation contain the release notes for any later releases.
.. toctree::
:maxdepth: 1
+ 3.1.10
3.1.9
3.1.8
3.1.7
@@ -71,6 +72,7 @@ versions of the documentation contain the release notes for any later releases.
.. toctree::
:maxdepth: 1
+ 2.2.22
2.2.21
2.2.20
2.2.19
diff --git a/tests/validators/tests.py b/tests/validators/tests.py
index d6d013c026..09d5c40ff5 100644
--- a/tests/validators/tests.py
+++ b/tests/validators/tests.py
@@ -226,9 +226,15 @@ TEST_DATA = [
(URLValidator(), None, ValidationError),
(URLValidator(), 56, ValidationError),
(URLValidator(), 'no_scheme', ValidationError),
- # Trailing newlines not accepted
+ # Newlines and tabs are not accepted.
(URLValidator(), 'http://www.djangoproject.com/\n', ValidationError),
(URLValidator(), 'http://[::ffff:192.9.5.5]\n', ValidationError),
+ (URLValidator(), 'http://www.djangoproject.com/\r', ValidationError),
+ (URLValidator(), 'http://[::ffff:192.9.5.5]\r', ValidationError),
+ (URLValidator(), 'http://www.django\rproject.com/', ValidationError),
+ (URLValidator(), 'http://[::\rffff:192.9.5.5]', ValidationError),
+ (URLValidator(), 'http://\twww.djangoproject.com/', ValidationError),
+ (URLValidator(), 'http://\t[::ffff:192.9.5.5]', ValidationError),
# Trailing junk does not take forever to reject
(URLValidator(), 'http://www.asdasdasdasdsadfm.com.br ', ValidationError),
(URLValidator(), 'http://www.asdasdasdasdsadfm.com.br z', ValidationError),