1
0
mirror of https://github.com/django/django.git synced 2025-06-05 03:29:12 +00:00

[1.4.x] Fixed a security issue in http redirects. Disclosure and new release forthcoming.

Backport of 4129201c3e0fa057c198bdefcb34686a23b4a93c from master.
This commit is contained in:
Florian Apolloner 2012-07-30 22:03:09 +02:00
parent c14f325c4e
commit e34685034b
2 changed files with 31 additions and 14 deletions

View File

@ -9,7 +9,7 @@ import warnings
from pprint import pformat from pprint import pformat
from urllib import urlencode, quote from urllib import urlencode, quote
from urlparse import urljoin from urlparse import urljoin, urlparse
try: try:
from cStringIO import StringIO from cStringIO import StringIO
except ImportError: except ImportError:
@ -114,7 +114,7 @@ class CompatCookie(SimpleCookie):
from django.conf import settings from django.conf import settings
from django.core import signing from django.core import signing
from django.core.exceptions import ImproperlyConfigured from django.core.exceptions import ImproperlyConfigured, SuspiciousOperation
from django.core.files import uploadhandler from django.core.files import uploadhandler
from django.http.multipartparser import MultiPartParser from django.http.multipartparser import MultiPartParser
from django.http.utils import * from django.http.utils import *
@ -731,20 +731,22 @@ class HttpResponse(object):
raise Exception("This %s instance cannot tell its position" % self.__class__) raise Exception("This %s instance cannot tell its position" % self.__class__)
return sum([len(str(chunk)) for chunk in self._container]) return sum([len(str(chunk)) for chunk in self._container])
class HttpResponseRedirect(HttpResponse): class HttpResponseRedirectBase(HttpResponse):
allowed_schemes = ['http', 'https', 'ftp']
def __init__(self, redirect_to):
super(HttpResponseRedirectBase, self).__init__()
parsed = urlparse(redirect_to)
if parsed.scheme and parsed.scheme not in self.allowed_schemes:
raise SuspiciousOperation("Unsafe redirect to URL with scheme '%s'" % parsed.scheme)
self['Location'] = iri_to_uri(redirect_to)
class HttpResponseRedirect(HttpResponseRedirectBase):
status_code = 302 status_code = 302
def __init__(self, redirect_to): class HttpResponsePermanentRedirect(HttpResponseRedirectBase):
super(HttpResponseRedirect, self).__init__()
self['Location'] = iri_to_uri(redirect_to)
class HttpResponsePermanentRedirect(HttpResponse):
status_code = 301 status_code = 301
def __init__(self, redirect_to):
super(HttpResponsePermanentRedirect, self).__init__()
self['Location'] = iri_to_uri(redirect_to)
class HttpResponseNotModified(HttpResponse): class HttpResponseNotModified(HttpResponse):
status_code = 304 status_code = 304

View File

@ -1,8 +1,11 @@
import copy import copy
import pickle import pickle
from django.http import (QueryDict, HttpResponse, SimpleCookie, BadHeaderError, from django.core.exceptions import SuspiciousOperation
parse_cookie) from django.http import (QueryDict, HttpResponse, HttpResponseRedirect,
HttpResponsePermanentRedirect,
SimpleCookie, BadHeaderError,
parse_cookie)
from django.utils import unittest from django.utils import unittest
@ -296,6 +299,18 @@ class HttpResponseTests(unittest.TestCase):
self.assertRaises(UnicodeEncodeError, self.assertRaises(UnicodeEncodeError,
getattr, r, 'content') getattr, r, 'content')
def test_unsafe_redirect(self):
bad_urls = [
'data:text/html,<script>window.alert("xss")</script>',
'mailto:test@example.com',
'file:///etc/passwd',
]
for url in bad_urls:
self.assertRaises(SuspiciousOperation,
HttpResponseRedirect, url)
self.assertRaises(SuspiciousOperation,
HttpResponsePermanentRedirect, url)
class CookieTests(unittest.TestCase): class CookieTests(unittest.TestCase):
def test_encode(self): def test_encode(self):
""" """