티스토리 수익 글 보기

티스토리 수익 글 보기

[1.6.x] Added additional checks in is_safe_url to account for flexibl… · django/django@6011075 · GitHub
Skip to content
/ django Public

Commit 6011075

Browse files
mxsashaapollo13
authored andcommitted
[1.6.x] Added additional checks in is_safe_url to account for flexible parsing.
This is a security fix. Disclosure following shortly.
1 parent 1abcf3a commit 6011075

File tree

3 files changed

+49
4
lines changed

3 files changed

+49
4
lines changed

django/contrib/auth/tests/test_views.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -444,8 +444,10 @@ def test_security_check(self, password='password'):
444444

445445
# Those URLs should not pass the security check
446446
for bad_url in ('http://example.com',
447+
'http:///example.com',
447448
'https://example.com',
448449
'ftp://exampel.com',
450+
'///example.com',
449451
'//example.com',
450452
'javascript:alert("XSS")'):
451453

@@ -467,8 +469,8 @@ def test_security_check(self, password='password'):
467469
'/view/?param=https://example.com',
468470
'/view?param=ftp://exampel.com',
469471
'view/?param=//example.com',
470-
'https:///',
471-
'HTTPS:///',
472+
'https://testserver/',
473+
'HTTPS://testserver/',
472474
'//testserver/',
473475
'/url%20with%20spaces/'): # see ticket #12534
474476
safe_url = '%(url)s?%(next)s=%(good_url)s' % {
@@ -660,8 +662,10 @@ def test_security_check(self, password='password'):
660662

661663
# Those URLs should not pass the security check
662664
for bad_url in ('http://example.com',
665+
'http:///example.com',
663666
'https://example.com',
664667
'ftp://exampel.com',
668+
'///example.com',
665669
'//example.com',
666670
'javascript:alert("XSS")'):
667671
nasty_url = '%(url)s?%(next)s=%(bad_url)s' % {
@@ -681,8 +685,8 @@ def test_security_check(self, password='password'):
681685
'/view/?param=https://example.com',
682686
'/view?param=ftp://exampel.com',
683687
'view/?param=//example.com',
684-
'https:///',
685-
'HTTPS:///',
688+
'https://testserver/',
689+
'HTTPS://testserver/',
686690
'//testserver/',
687691
'/url%20with%20spaces/'): # see ticket #12534
688692
safe_url = '%(url)s?%(next)s=%(good_url)s' % {

django/utils/http.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,18 @@ def is_safe_url(url, host=None):
256256
"""
257257
if not url:
258258
return False
259+
# Chrome treats \ completely as /
260+
url = url.replace('\\', '/')
261+
# Chrome considers any URL with more than two slashes to be absolute, but
262+
# urlaprse is not so flexible. Treat any url with three slashes as unsafe.
263+
if url.startswith('///'):
264+
return False
259265
url_info = urlparse(url)
266+
# Forbid URLs like http:///example.com - with a scheme, but without a hostname.
267+
# In that URL, example.com is not the hostname but, a path component. However,
268+
# Chrome will still consider example.com to be the hostname, so we must not
269+
# allow this syntax.
270+
if not url_info.netloc and url_info.scheme:
271+
return False
260272
return (not url_info.netloc or url_info.netloc == host) and \
261273
(not url_info.scheme or url_info.scheme in ['http', 'https'])

tests/utils_tests/test_http.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,35 @@ def test_base36(self):
9191
self.assertEqual(http.int_to_base36(n), b36)
9292
self.assertEqual(http.base36_to_int(b36), n)
9393

94+
def test_is_safe_url(self):
95+
for bad_url in ('http://example.com',
96+
'http:///example.com',
97+
'https://example.com',
98+
'ftp://exampel.com',
99+
r'\\example.com',
100+
r'\\\example.com',
101+
r'/\\/example.com',
102+
r'\\\example.com',
103+
r'\\example.com',
104+
r'\\//example.com',
105+
r'/\/example.com',
106+
r'\/example.com',
107+
r'/\example.com',
108+
'http:///example.com',
109+
'http:/\//example.com',
110+
'http:\/example.com',
111+
'http:/\example.com',
112+
'javascript:alert("XSS")'):
113+
self.assertFalse(http.is_safe_url(bad_url, host='testserver'), "%s should be blocked" % bad_url)
114+
for good_url in ('/view/?param=http://example.com',
115+
'/view/?param=https://example.com',
116+
'/view?param=ftp://exampel.com',
117+
'view/?param=//example.com',
118+
'https://testserver/',
119+
'HTTPS://testserver/',
120+
'//testserver/',
121+
'/url%20with%20spaces/'):
122+
self.assertTrue(http.is_safe_url(good_url, host='testserver'), "%s should be allowed" % good_url)
94123

95124
class ETagProcessingTests(unittest.TestCase):
96125
def testParsing(self):

0 commit comments

Comments
 (0)