CVE-2016-2512
Description
The utils.http.is_safe_url function in Django before 1.8.10 and 1.9.x before 1.9.3 allows remote attackers to redirect users to arbitrary web sites and conduct phishing attacks or possibly conduct cross-site scripting (XSS) attacks via a URL containing basic authentication, as demonstrated by http://mysite.example.com\@attacker.com.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
DjangoPyPI | < 1.8.10 | 1.8.10 |
DjangoPyPI | >= 1.9a1, < 1.9.3 | 1.9.3 |
Affected products
4cpe:2.3:a:djangoproject:django:1.8.9:*:*:*:*:*:*:*+ 3 more
- cpe:2.3:a:djangoproject:django:1.8.9:*:*:*:*:*:*:*
- cpe:2.3:a:djangoproject:django:1.9:*:*:*:*:*:*:*
- cpe:2.3:a:djangoproject:django:1.9.1:*:*:*:*:*:*:*
- cpe:2.3:a:djangoproject:django:1.9.2:*:*:*:*:*:*:*
Patches
3382ab1373129[1.8.x] Fixed CVE-2016-2512 -- Prevented spoofing is_safe_url() with basic auth.
3 files changed · +34 −2
django/utils/http.py+6 −2 modified@@ -277,8 +277,12 @@ def is_safe_url(url, host=None): url = url.strip() if not url: return False - # Chrome treats \ completely as / - url = url.replace('\\', '/') + # Chrome treats \ completely as / in paths but it could be part of some + # basic auth credentials so we need to check both URLs. + return _is_safe_url(url, host) and _is_safe_url(url.replace('\\', '/'), host) + + +def _is_safe_url(url, host): # Chrome considers any URL with more than two slashes to be absolute, but # urlparse is not so flexible. Treat any url with three slashes as unsafe. if url.startswith('///'):
docs/releases/1.8.10.txt+16 −0 modified@@ -6,6 +6,22 @@ Django 1.8.10 release notes Django 1.8.10 fixes two security issues and several bugs in 1.8.9. +CVE-2016-2512: Malicious redirect and possible XSS attack via user-supplied redirect URLs containing basic auth +=============================================================================================================== + +Django relies on user input in some cases (e.g. +:func:`django.contrib.auth.views.login` and :doc:`i18n </topics/i18n/index>`) +to redirect the user to an "on success" URL. The security check for these +redirects (namely ``django.utils.http.is_safe_url()``) considered some URLs +with basic authentication credentials "safe" when they shouldn't be. + +For example, a URL like ``http://mysite.example.com\@attacker.com`` would be +considered safe if the request's host is ``http://mysite.example.com``, but +redirecting to this URL sends the user to ``attacker.com``. + +Also, if a developer relies on ``is_safe_url()`` to provide safe redirect +targets and puts such a URL into a link, they could suffer from an XSS attack. + Bugfixes ========
tests/utils_tests/test_http.py+12 −0 modified@@ -117,6 +117,11 @@ def test_is_safe_url(self): 'javascript:alert("XSS")', '\njavascript:alert(x)', '\x08//example.com', + r'http://otherserver\@example.com', + r'http:\\testserver\@example.com', + r'http://testserver\me:pass@example.com', + r'http://testserver\@example.com', + r'http:\\testserver\confirm\me@example.com', '\n'): self.assertFalse(http.is_safe_url(bad_url, host='testserver'), "%s should be blocked" % bad_url) for good_url in ('/view/?param=http://example.com', @@ -126,8 +131,15 @@ def test_is_safe_url(self): 'https://testserver/', 'HTTPS://testserver/', '//testserver/', + 'http://testserver/confirm?email=me@example.com', '/url%20with%20spaces/'): self.assertTrue(http.is_safe_url(good_url, host='testserver'), "%s should be allowed" % good_url) + # Valid basic auth credentials are allowed. + self.assertTrue(http.is_safe_url(r'http://user:pass@testserver/', host='user:pass@testserver')) + # A path without host is allowed. + self.assertTrue(http.is_safe_url('/confirm/me@example.com')) + # Basic auth without host is not allowed. + self.assertFalse(http.is_safe_url(r'http://testserver\@example.com')) def test_urlsafe_base64_roundtrip(self): bytestring = b'foo'
fc6d147a63f8[1.9.x] Fixed CVE-2016-2512 -- Prevented spoofing is_safe_url() with basic auth.
4 files changed · +50 −2
django/utils/http.py+6 −2 modified@@ -290,8 +290,12 @@ def is_safe_url(url, host=None): url = url.strip() if not url: return False - # Chrome treats \ completely as / - url = url.replace('\\', '/') + # Chrome treats \ completely as / in paths but it could be part of some + # basic auth credentials so we need to check both URLs. + return _is_safe_url(url, host) and _is_safe_url(url.replace('\\', '/'), host) + + +def _is_safe_url(url, host): # Chrome considers any URL with more than two slashes to be absolute, but # urlparse is not so flexible. Treat any url with three slashes as unsafe. if url.startswith('///'):
docs/releases/1.8.10.txt+16 −0 modified@@ -6,6 +6,22 @@ Django 1.8.10 release notes Django 1.8.10 fixes two security issues and several bugs in 1.8.9. +CVE-2016-2512: Malicious redirect and possible XSS attack via user-supplied redirect URLs containing basic auth +=============================================================================================================== + +Django relies on user input in some cases (e.g. +:func:`django.contrib.auth.views.login` and :doc:`i18n </topics/i18n/index>`) +to redirect the user to an "on success" URL. The security check for these +redirects (namely ``django.utils.http.is_safe_url()``) considered some URLs +with basic authentication credentials "safe" when they shouldn't be. + +For example, a URL like ``http://mysite.example.com\@attacker.com`` would be +considered safe if the request's host is ``http://mysite.example.com``, but +redirecting to this URL sends the user to ``attacker.com``. + +Also, if a developer relies on ``is_safe_url()`` to provide safe redirect +targets and puts such a URL into a link, they could suffer from an XSS attack. + Bugfixes ========
docs/releases/1.9.3.txt+16 −0 modified@@ -6,6 +6,22 @@ Django 1.9.3 release notes Django 1.9.3 fixes two security issues and several bugs in 1.9.2. +CVE-2016-2512: Malicious redirect and possible XSS attack via user-supplied redirect URLs containing basic auth +=============================================================================================================== + +Django relies on user input in some cases (e.g. +:func:`django.contrib.auth.views.login` and :doc:`i18n </topics/i18n/index>`) +to redirect the user to an "on success" URL. The security check for these +redirects (namely ``django.utils.http.is_safe_url()``) considered some URLs +with basic authentication credentials "safe" when they shouldn't be. + +For example, a URL like ``http://mysite.example.com\@attacker.com`` would be +considered safe if the request's host is ``http://mysite.example.com``, but +redirecting to this URL sends the user to ``attacker.com``. + +Also, if a developer relies on ``is_safe_url()`` to provide safe redirect +targets and puts such a URL into a link, they could suffer from an XSS attack. + Bugfixes ========
tests/utils_tests/test_http.py+12 −0 modified@@ -92,6 +92,11 @@ def test_is_safe_url(self): 'javascript:alert("XSS")', '\njavascript:alert(x)', '\x08//example.com', + r'http://otherserver\@example.com', + r'http:\\testserver\@example.com', + r'http://testserver\me:pass@example.com', + r'http://testserver\@example.com', + r'http:\\testserver\confirm\me@example.com', '\n'): self.assertFalse(http.is_safe_url(bad_url, host='testserver'), "%s should be blocked" % bad_url) for good_url in ('/view/?param=http://example.com', @@ -101,8 +106,15 @@ def test_is_safe_url(self): 'https://testserver/', 'HTTPS://testserver/', '//testserver/', + 'http://testserver/confirm?email=me@example.com', '/url%20with%20spaces/'): self.assertTrue(http.is_safe_url(good_url, host='testserver'), "%s should be allowed" % good_url) + # Valid basic auth credentials are allowed. + self.assertTrue(http.is_safe_url(r'http://user:pass@testserver/', host='user:pass@testserver')) + # A path without host is allowed. + self.assertTrue(http.is_safe_url('/confirm/me@example.com')) + # Basic auth without host is not allowed. + self.assertFalse(http.is_safe_url(r'http://testserver\@example.com')) def test_urlsafe_base64_roundtrip(self): bytestring = b'foo'
c5544d289233Fixed CVE-2016-2512 -- Prevented spoofing is_safe_url() with basic auth.
4 files changed · +50 −2
django/utils/http.py+6 −2 modified@@ -290,8 +290,12 @@ def is_safe_url(url, host=None): url = url.strip() if not url: return False - # Chrome treats \ completely as / - url = url.replace('\\', '/') + # Chrome treats \ completely as / in paths but it could be part of some + # basic auth credentials so we need to check both URLs. + return _is_safe_url(url, host) and _is_safe_url(url.replace('\\', '/'), host) + + +def _is_safe_url(url, host): # Chrome considers any URL with more than two slashes to be absolute, but # urlparse is not so flexible. Treat any url with three slashes as unsafe. if url.startswith('///'):
docs/releases/1.8.10.txt+16 −0 modified@@ -6,6 +6,22 @@ Django 1.8.10 release notes Django 1.8.10 fixes two security issues and several bugs in 1.8.9. +CVE-2016-2512: Malicious redirect and possible XSS attack via user-supplied redirect URLs containing basic auth +=============================================================================================================== + +Django relies on user input in some cases (e.g. +:func:`django.contrib.auth.views.login` and :doc:`i18n </topics/i18n/index>`) +to redirect the user to an "on success" URL. The security check for these +redirects (namely ``django.utils.http.is_safe_url()``) considered some URLs +with basic authentication credentials "safe" when they shouldn't be. + +For example, a URL like ``http://mysite.example.com\@attacker.com`` would be +considered safe if the request's host is ``http://mysite.example.com``, but +redirecting to this URL sends the user to ``attacker.com``. + +Also, if a developer relies on ``is_safe_url()`` to provide safe redirect +targets and puts such a URL into a link, they could suffer from an XSS attack. + Bugfixes ========
docs/releases/1.9.3.txt+16 −0 modified@@ -6,6 +6,22 @@ Django 1.9.3 release notes Django 1.9.3 fixes two security issues and several bugs in 1.9.2. +CVE-2016-2512: Malicious redirect and possible XSS attack via user-supplied redirect URLs containing basic auth +=============================================================================================================== + +Django relies on user input in some cases (e.g. +:func:`django.contrib.auth.views.login` and :doc:`i18n </topics/i18n/index>`) +to redirect the user to an "on success" URL. The security check for these +redirects (namely ``django.utils.http.is_safe_url()``) considered some URLs +with basic authentication credentials "safe" when they shouldn't be. + +For example, a URL like ``http://mysite.example.com\@attacker.com`` would be +considered safe if the request's host is ``http://mysite.example.com``, but +redirecting to this URL sends the user to ``attacker.com``. + +Also, if a developer relies on ``is_safe_url()`` to provide safe redirect +targets and puts such a URL into a link, they could suffer from an XSS attack. + Bugfixes ========
tests/utils_tests/test_http.py+12 −0 modified@@ -97,6 +97,11 @@ def test_is_safe_url(self): 'javascript:alert("XSS")', '\njavascript:alert(x)', '\x08//example.com', + r'http://otherserver\@example.com', + r'http:\\testserver\@example.com', + r'http://testserver\me:pass@example.com', + r'http://testserver\@example.com', + r'http:\\testserver\confirm\me@example.com', '\n'): self.assertFalse(http.is_safe_url(bad_url, host='testserver'), "%s should be blocked" % bad_url) for good_url in ('/view/?param=http://example.com', @@ -106,8 +111,15 @@ def test_is_safe_url(self): 'https://testserver/', 'HTTPS://testserver/', '//testserver/', + 'http://testserver/confirm?email=me@example.com', '/url%20with%20spaces/'): self.assertTrue(http.is_safe_url(good_url, host='testserver'), "%s should be allowed" % good_url) + # Valid basic auth credentials are allowed. + self.assertTrue(http.is_safe_url(r'http://user:pass@testserver/', host='user:pass@testserver')) + # A path without host is allowed. + self.assertTrue(http.is_safe_url('/confirm/me@example.com')) + # Basic auth without host is not allowed. + self.assertFalse(http.is_safe_url(r'http://testserver\@example.com')) def test_urlsafe_base64_roundtrip(self): bytestring = b'foo'
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
21- github.com/advisories/GHSA-pw27-w7w4-9qc7ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2016-2512ghsaADVISORY
- www.djangoproject.com/weblog/2016/mar/01/security-releases/nvdVendor Advisory
- rhn.redhat.com/errata/RHSA-2016-0502.htmlnvdWEB
- rhn.redhat.com/errata/RHSA-2016-0504.htmlnvdWEB
- rhn.redhat.com/errata/RHSA-2016-0505.htmlnvdWEB
- rhn.redhat.com/errata/RHSA-2016-0506.htmlnvdWEB
- www.debian.org/security/2016/dsa-3544nvdWEB
- www.oracle.com/technetwork/topics/security/bulletinapr2016-2952098.htmlnvdWEB
- www.ubuntu.com/usn/USN-2915-1nvdWEB
- www.ubuntu.com/usn/USN-2915-2nvdWEB
- www.ubuntu.com/usn/USN-2915-3nvdWEB
- github.com/django/django/commit/382ab137312961ad62feb8109d70a5a581fe8350ghsaWEB
- github.com/django/django/commit/c5544d289233f501917e25970c03ed444abbd4f0nvdWEB
- github.com/django/django/commit/fc6d147a63f89795dbcdecb0559256470fff4380ghsaWEB
- github.com/pypa/advisory-database/tree/main/vulns/django/PYSEC-2016-15.yamlghsaWEB
- web.archive.org/web/20210123090815/http://www.securityfocus.com/bid/83879ghsaWEB
- web.archive.org/web/20210413200202/http://www.securitytracker.com/id/1035152ghsaWEB
- www.djangoproject.com/weblog/2016/mar/01/security-releasesghsaWEB
- www.securityfocus.com/bid/83879nvd
- www.securitytracker.com/id/1035152nvd
News mentions
0No linked articles in our index yet.