diff options
| author | Mariusz Felisiak <felisiak.mariusz@gmail.com> | 2021-05-04 20:50:12 +0200 |
|---|---|---|
| committer | Mariusz Felisiak <felisiak.mariusz@gmail.com> | 2021-05-06 08:53:27 +0200 |
| commit | d9594c4ea57b6309d93879805302cec9ae9f23ff (patch) | |
| tree | f9ca2e6002625d83d4def0a0a25546a0cbab1d59 | |
| parent | 163700388cda2305c8dbcdb3ac1542a442f3e955 (diff) | |
[2.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.py | 5 | ||||
| -rw-r--r-- | docs/releases/2.2.22.txt | 22 | ||||
| -rw-r--r-- | docs/releases/index.txt | 1 | ||||
| -rw-r--r-- | tests/validators/tests.py | 8 |
4 files changed, 34 insertions, 2 deletions
diff --git a/django/core/validators.py b/django/core/validators.py index 38e4b6aa1d..d32b54f68c 100644 --- a/django/core/validators.py +++ b/django/core/validators.py @@ -101,6 +101,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) @@ -108,7 +109,9 @@ class URLValidator(RegexValidator): 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) 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/index.txt b/docs/releases/index.txt index e59c97b17f..4262a97ac1 100644 --- a/docs/releases/index.txt +++ b/docs/releases/index.txt @@ -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 diff --git a/tests/validators/tests.py b/tests/validators/tests.py index 36d0b2a520..012b098f4e 100644 --- a/tests/validators/tests.py +++ b/tests/validators/tests.py @@ -222,9 +222,15 @@ TEST_DATA = [ (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), |
