+19
-3
lines changedFilter options
+19
-3
lines changed Original file line number Diff line number Diff line change
@@ -234,6 +234,7 @@ def is_safe_url(url, host=None):
234
234
"""
235
235
if not url:
236
236
return False
237
+
url = url.strip()
237
238
# Chrome treats \ completely as /
238
239
url = url.replace('\\', '/')
239
240
# Chrome considers any URL with more than two slashes to be absolute, but
Original file line number Diff line number Diff line change
@@ -31,6 +31,20 @@ development server now does the same. Django's development server is not
31
31
recommended for production use, but matching the behavior of common production
32
32
servers reduces the surface area for behavior changes during deployment.
33
33
34
+
Mitigated possible XSS attack via user-supplied redirect URLs
35
+
=============================================================
36
+
37
+
Django relies on user input in some cases (e.g.
38
+
:func:`django.contrib.auth.views.login` and :doc:`i18n </topics/i18n/index>`)
39
+
to redirect the user to an "on success" URL. The security checks for these
40
+
redirects (namely ``django.util.http.is_safe_url()``) didn't strip leading
41
+
whitespace on the tested URL and as such considered URLs like
42
+
``\njavascript:...`` safe. If a developer relied on ``is_safe_url()`` to
43
+
provide safe redirect targets and put such a URL into a link, they could suffer
44
+
from a XSS attack. This bug doesn't affect Django currently, since we only put
45
+
this URL into the ``Location`` response header and browsers seem to ignore
46
+
JavaScript there.
47
+
34
48
Bugfixes
35
49
========
36
50
Original file line number Diff line number Diff line change
@@ -64,7 +64,7 @@ def test_base36(self):
64
64
# bad input
65
65
for n in [-1, sys.maxint+1, '1', 'foo', {1:2}, (1,2,3)]:
66
66
self.assertRaises(ValueError, http.int_to_base36, n)
67
-
67
+
68
68
for n in ['#', ' ']:
69
69
self.assertRaises(ValueError, http.base36_to_int, n)
70
70
@@ -73,7 +73,7 @@ def test_base36(self):
73
73
74
74
# non-integer input
75
75
self.assertRaises(TypeError, http.int_to_base36, 3.141)
76
-
76
+
77
77
# more explicit output testing
78
78
for n, b36 in [(0, '0'), (1, '1'), (42, '16'), (818469960, 'django')]:
79
79
self.assertEqual(http.int_to_base36(n), b36)
@@ -97,7 +97,8 @@ def test_is_safe_url(self):
97
97
'http:/\//example.com',
98
98
'http:\/example.com',
99
99
'http:/\example.com',
100
-
'javascript:alert("XSS")'):
100
+
'javascript:alert("XSS")'
101
+
'\njavascript:alert(x)'):
101
102
self.assertFalse(http.is_safe_url(bad_url, host='testserver'), "%s should be blocked" % bad_url)
102
103
for good_url in ('/view/?param=http://example.com',
103
104
'/view/?param=https://example.com',
You can’t perform that action at this time.
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4