Commit e3468503 authored by Florian Apolloner's avatar Florian Apolloner
Browse files

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

Backport of 4129201c from master.
parent c14f325c
Loading
Loading
Loading
Loading
+12 −10
Original line number Diff line number Diff line
@@ -9,7 +9,7 @@ import warnings

from pprint import pformat
from urllib import urlencode, quote
from urlparse import urljoin
from urlparse import urljoin, urlparse
try:
    from cStringIO import StringIO
except ImportError:
@@ -114,7 +114,7 @@ class CompatCookie(SimpleCookie):

from django.conf import settings
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.http.multipartparser import MultiPartParser
from django.http.utils import *
@@ -731,19 +731,21 @@ class HttpResponse(object):
            raise Exception("This %s instance cannot tell its position" % self.__class__)
        return sum([len(str(chunk)) for chunk in self._container])

class HttpResponseRedirect(HttpResponse):
    status_code = 302
class HttpResponseRedirectBase(HttpResponse):
    allowed_schemes = ['http', 'https', 'ftp']

    def __init__(self, redirect_to):
        super(HttpResponseRedirect, self).__init__()
        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 HttpResponsePermanentRedirect(HttpResponse):
    status_code = 301
class HttpResponseRedirect(HttpResponseRedirectBase):
    status_code = 302

    def __init__(self, redirect_to):
        super(HttpResponsePermanentRedirect, self).__init__()
        self['Location'] = iri_to_uri(redirect_to)
class HttpResponsePermanentRedirect(HttpResponseRedirectBase):
    status_code = 301

class HttpResponseNotModified(HttpResponse):
    status_code = 304
+17 −2
Original line number Diff line number Diff line
import copy
import pickle

from django.http import (QueryDict, HttpResponse, SimpleCookie, BadHeaderError,
from django.core.exceptions import SuspiciousOperation
from django.http import (QueryDict, HttpResponse, HttpResponseRedirect,
                         HttpResponsePermanentRedirect,
                         SimpleCookie, BadHeaderError,
                         parse_cookie)
from django.utils import unittest

@@ -296,6 +299,18 @@ class HttpResponseTests(unittest.TestCase):
        self.assertRaises(UnicodeEncodeError,
                          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):
    def test_encode(self):
        """