Skip to content

Commit

Permalink
[2.2.x] Fixed #32713, Fixed CVE-2021-32052 -- Prevented newlines and …
Browse files Browse the repository at this point in the history
…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] python/cpython@76cd81d

Backport of e1e81aa from main.
  • Loading branch information
felixxm committed May 6, 2021
1 parent 1637003 commit d9594c4
Show file tree
Hide file tree
Showing 4 changed files with 34 additions and 2 deletions.
5 changes: 4 additions & 1 deletion django/core/validators.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,14 +101,17 @@ 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)
if schemes is not None:
self.schemes = schemes

def __call__(self, value):
# Check first if the scheme is valid
if isinstance(value, str) and self.unsafe_chars.intersection(value):
raise ValidationError(self.message, code=self.code)
# Check if the scheme is valid.
scheme = value.split('://')[0].lower()
if scheme not in self.schemes:
raise ValidationError(self.message, code=self.code)
Expand Down
22 changes: 22 additions & 0 deletions docs/releases/2.2.22.txt
Original file line number Diff line number Diff line change
@@ -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.
1 change: 1 addition & 0 deletions docs/releases/index.txt
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,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
Expand Down
8 changes: 7 additions & 1 deletion tests/validators/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -222,9 +222,15 @@
(URLValidator(EXTENDED_SCHEMES), 'git+ssh://git@github.com/example/hg-git.git', None),

(URLValidator(EXTENDED_SCHEMES), 'git://-invalid.com', 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),
Expand Down

0 comments on commit d9594c4

Please sign in to comment.