Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

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

…release forthcoming.

Backport of 4129201 from master.
  • Loading branch information...
commit e34685034b60be1112160e76091e5aee60149fa1 1 parent c14f325
Florian Apolloner authored July 30, 2012
22  django/http/__init__.py
@@ -9,7 +9,7 @@
9 9
 
10 10
 from pprint import pformat
11 11
 from urllib import urlencode, quote
12  
-from urlparse import urljoin
  12
+from urlparse import urljoin, urlparse
13 13
 try:
14 14
     from cStringIO import StringIO
15 15
 except ImportError:
@@ -114,7 +114,7 @@ def __init__(self, *args, **kwargs):
114 114
 
115 115
 from django.conf import settings
116 116
 from django.core import signing
117  
-from django.core.exceptions import ImproperlyConfigured
  117
+from django.core.exceptions import ImproperlyConfigured, SuspiciousOperation
118 118
 from django.core.files import uploadhandler
119 119
 from django.http.multipartparser import MultiPartParser
120 120
 from django.http.utils import *
@@ -731,19 +731,21 @@ def tell(self):
731 731
             raise Exception("This %s instance cannot tell its position" % self.__class__)
732 732
         return sum([len(str(chunk)) for chunk in self._container])
733 733
 
734  
-class HttpResponseRedirect(HttpResponse):
735  
-    status_code = 302
  734
+class HttpResponseRedirectBase(HttpResponse):
  735
+    allowed_schemes = ['http', 'https', 'ftp']
736 736
 
737 737
     def __init__(self, redirect_to):
738  
-        super(HttpResponseRedirect, self).__init__()
  738
+        super(HttpResponseRedirectBase, self).__init__()
  739
+        parsed = urlparse(redirect_to)
  740
+        if parsed.scheme and parsed.scheme not in self.allowed_schemes:
  741
+            raise SuspiciousOperation("Unsafe redirect to URL with scheme '%s'" % parsed.scheme)
739 742
         self['Location'] = iri_to_uri(redirect_to)
740 743
 
741  
-class HttpResponsePermanentRedirect(HttpResponse):
742  
-    status_code = 301
  744
+class HttpResponseRedirect(HttpResponseRedirectBase):
  745
+    status_code = 302
743 746
 
744  
-    def __init__(self, redirect_to):
745  
-        super(HttpResponsePermanentRedirect, self).__init__()
746  
-        self['Location'] = iri_to_uri(redirect_to)
  747
+class HttpResponsePermanentRedirect(HttpResponseRedirectBase):
  748
+    status_code = 301
747 749
 
748 750
 class HttpResponseNotModified(HttpResponse):
749 751
     status_code = 304
19  tests/regressiontests/httpwrappers/tests.py
... ...
@@ -1,8 +1,11 @@
1 1
 import copy
2 2
 import pickle
3 3
 
4  
-from django.http import (QueryDict, HttpResponse, SimpleCookie, BadHeaderError,
5  
-        parse_cookie)
  4
+from django.core.exceptions import SuspiciousOperation
  5
+from django.http import (QueryDict, HttpResponse, HttpResponseRedirect,
  6
+                         HttpResponsePermanentRedirect,
  7
+                         SimpleCookie, BadHeaderError,
  8
+                         parse_cookie)
6 9
 from django.utils import unittest
7 10
 
8 11
 
@@ -296,6 +299,18 @@ def test_iter_content(self):
296 299
         self.assertRaises(UnicodeEncodeError,
297 300
                           getattr, r, 'content')
298 301
 
  302
+    def test_unsafe_redirect(self):
  303
+        bad_urls = [
  304
+            'data:text/html,<script>window.alert("xss")</script>',
  305
+            'mailto:test@example.com',
  306
+            'file:///etc/passwd',
  307
+        ]
  308
+        for url in bad_urls:
  309
+            self.assertRaises(SuspiciousOperation,
  310
+                              HttpResponseRedirect, url)
  311
+            self.assertRaises(SuspiciousOperation,
  312
+                              HttpResponsePermanentRedirect, url)
  313
+
299 314
 class CookieTests(unittest.TestCase):
300 315
     def test_encode(self):
301 316
         """

0 notes on commit e346850

Please sign in to comment.
Something went wrong with that request. Please try again.