Author: russellm
Date: 2011-09-09 18:07:50 -0700 (Fri, 09 Sep 2011)
New Revision: 16761

Modified:
   django/branches/releases/1.3.X/django/conf/global_settings.py
   django/branches/releases/1.3.X/django/http/__init__.py
   django/branches/releases/1.3.X/docs/ref/request-response.txt
   django/branches/releases/1.3.X/docs/ref/settings.txt
   django/branches/releases/1.3.X/tests/regressiontests/requests/tests.py
Log:
[1.3.X] Added protection against spoofing of X_FORWARDED_HOST headers. A 
security announcement will be made shortly.

Backport of r16758 from trunk.

Modified: django/branches/releases/1.3.X/django/conf/global_settings.py
===================================================================
--- django/branches/releases/1.3.X/django/conf/global_settings.py       
2011-09-10 00:47:00 UTC (rev 16760)
+++ django/branches/releases/1.3.X/django/conf/global_settings.py       
2011-09-10 01:07:50 UTC (rev 16761)
@@ -399,6 +399,8 @@
 DEFAULT_TABLESPACE = ''
 DEFAULT_INDEX_TABLESPACE = ''
 
+USE_X_FORWARDED_HOST = False
+
 ##############
 # MIDDLEWARE #
 ##############

Modified: django/branches/releases/1.3.X/django/http/__init__.py
===================================================================
--- django/branches/releases/1.3.X/django/http/__init__.py      2011-09-10 
00:47:00 UTC (rev 16760)
+++ django/branches/releases/1.3.X/django/http/__init__.py      2011-09-10 
01:07:50 UTC (rev 16761)
@@ -153,7 +153,8 @@
     def get_host(self):
         """Returns the HTTP host using the environment or request headers."""
         # We try three options, in order of decreasing preference.
-        if 'HTTP_X_FORWARDED_HOST' in self.META:
+        if settings.USE_X_FORWARDED_HOST and (
+            'HTTP_X_FORWARDED_HOST' in self.META):
             host = self.META['HTTP_X_FORWARDED_HOST']
         elif 'HTTP_HOST' in self.META:
             host = self.META['HTTP_HOST']

Modified: django/branches/releases/1.3.X/docs/ref/request-response.txt
===================================================================
--- django/branches/releases/1.3.X/docs/ref/request-response.txt        
2011-09-10 00:47:00 UTC (rev 16760)
+++ django/branches/releases/1.3.X/docs/ref/request-response.txt        
2011-09-10 01:07:50 UTC (rev 16761)
@@ -191,10 +191,11 @@
 
 .. method:: HttpRequest.get_host()
 
-    Returns the originating host of the request using information from the
-    ``HTTP_X_FORWARDED_HOST`` and ``HTTP_HOST`` headers (in that order). If
-    they don't provide a value, the method uses a combination of
-    ``SERVER_NAME`` and ``SERVER_PORT`` as detailed in `PEP 333`_.
+    Returns the originating host of the request using information from
+    the ``HTTP_X_FORWARDED_HOST`` (if enabled in the settings) and 
``HTTP_HOST``
+    headers (in that order). If they don't provide a value, the method
+    uses a combination of ``SERVER_NAME`` and ``SERVER_PORT`` as
+    detailed in :pep:`3333`.
 
     .. _PEP 333: http://www.python.org/dev/peps/pep-0333/
 

Modified: django/branches/releases/1.3.X/docs/ref/settings.txt
===================================================================
--- django/branches/releases/1.3.X/docs/ref/settings.txt        2011-09-10 
00:47:00 UTC (rev 16760)
+++ django/branches/releases/1.3.X/docs/ref/settings.txt        2011-09-10 
01:07:50 UTC (rev 16761)
@@ -1960,6 +1960,19 @@
 
 See also :setting:`THOUSAND_SEPARATOR` and :setting:`NUMBER_GROUPING`.
 
+.. setting:: USE_X_FORWARDED_HOST
+
+USE_X_FORWARDED_HOST
+--------------------
+
+.. versionadded:: 1.3.1
+
+Default: ``False``
+
+A boolean that specifies whether to use the X-Forwarded-Host header in
+preference to the Host header. This should only be enabled if a proxy
+which sets this header is in use.
+
 .. setting:: YEAR_MONTH_FORMAT
 
 YEAR_MONTH_FORMAT

Modified: django/branches/releases/1.3.X/tests/regressiontests/requests/tests.py
===================================================================
--- django/branches/releases/1.3.X/tests/regressiontests/requests/tests.py      
2011-09-10 00:47:00 UTC (rev 16760)
+++ django/branches/releases/1.3.X/tests/regressiontests/requests/tests.py      
2011-09-10 01:07:50 UTC (rev 16761)
@@ -2,12 +2,14 @@
 from datetime import datetime, timedelta
 from StringIO import StringIO
 
+from django.conf import settings
 from django.core.handlers.modpython import ModPythonRequest
 from django.core.handlers.wsgi import WSGIRequest, LimitedStream
 from django.http import HttpRequest, HttpResponse, parse_cookie
 from django.utils import unittest
 from django.utils.http import cookie_date
 
+
 class RequestsTests(unittest.TestCase):
     def test_httprequest(self):
         request = HttpRequest()
@@ -58,6 +60,94 @@
         
self.assertEqual(request.build_absolute_uri(location="/path/with:colons"),
             'http://www.example.com/path/with:colons')
 
+    def test_http_get_host(self):
+        old_USE_X_FORWARDED_HOST = settings.USE_X_FORWARDED_HOST
+        try:
+            settings.USE_X_FORWARDED_HOST = False
+
+            # Check if X_FORWARDED_HOST is provided.
+            request = HttpRequest()
+            request.META = {
+                u'HTTP_X_FORWARDED_HOST': u'forward.com',
+                u'HTTP_HOST': u'example.com',
+                u'SERVER_NAME': u'internal.com',
+                u'SERVER_PORT': 80,
+            }
+            # X_FORWARDED_HOST is ignored.
+            self.assertEqual(request.get_host(), 'example.com')
+
+            # Check if X_FORWARDED_HOST isn't provided.
+            request = HttpRequest()
+            request.META = {
+                u'HTTP_HOST': u'example.com',
+                u'SERVER_NAME': u'internal.com',
+                u'SERVER_PORT': 80,
+            }
+            self.assertEqual(request.get_host(), 'example.com')
+
+            # Check if HTTP_HOST isn't provided.
+            request = HttpRequest()
+            request.META = {
+                u'SERVER_NAME': u'internal.com',
+                u'SERVER_PORT': 80,
+            }
+            self.assertEqual(request.get_host(), 'internal.com')
+
+            # Check if HTTP_HOST isn't provided, and we're on a nonstandard 
port
+            request = HttpRequest()
+            request.META = {
+                u'SERVER_NAME': u'internal.com',
+                u'SERVER_PORT': 8042,
+            }
+            self.assertEqual(request.get_host(), 'internal.com:8042')
+
+        finally:
+            settings.USE_X_FORWARDED_HOST = old_USE_X_FORWARDED_HOST
+
+    def test_http_get_host_with_x_forwarded_host(self):
+        old_USE_X_FORWARDED_HOST = settings.USE_X_FORWARDED_HOST
+        try:
+            settings.USE_X_FORWARDED_HOST = True
+
+            # Check if X_FORWARDED_HOST is provided.
+            request = HttpRequest()
+            request.META = {
+                u'HTTP_X_FORWARDED_HOST': u'forward.com',
+                u'HTTP_HOST': u'example.com',
+                u'SERVER_NAME': u'internal.com',
+                u'SERVER_PORT': 80,
+            }
+            # X_FORWARDED_HOST is obeyed.
+            self.assertEqual(request.get_host(), 'forward.com')
+
+            # Check if X_FORWARDED_HOST isn't provided.
+            request = HttpRequest()
+            request.META = {
+                u'HTTP_HOST': u'example.com',
+                u'SERVER_NAME': u'internal.com',
+                u'SERVER_PORT': 80,
+            }
+            self.assertEqual(request.get_host(), 'example.com')
+
+            # Check if HTTP_HOST isn't provided.
+            request = HttpRequest()
+            request.META = {
+                u'SERVER_NAME': u'internal.com',
+                u'SERVER_PORT': 80,
+            }
+            self.assertEqual(request.get_host(), 'internal.com')
+
+            # Check if HTTP_HOST isn't provided, and we're on a nonstandard 
port
+            request = HttpRequest()
+            request.META = {
+                u'SERVER_NAME': u'internal.com',
+                u'SERVER_PORT': 8042,
+            }
+            self.assertEqual(request.get_host(), 'internal.com:8042')
+
+        finally:
+            settings.USE_X_FORWARDED_HOST = old_USE_X_FORWARDED_HOST
+
     def test_near_expiration(self):
         "Cookie will expire when an near expiration time is provided"
         response = HttpResponse()

-- 
You received this message because you are subscribed to the Google Groups 
"Django updates" group.
To post to this group, send email to django-updates@googlegroups.com.
To unsubscribe from this group, send email to 
django-updates+unsubscr...@googlegroups.com.
For more options, visit this group at 
http://groups.google.com/group/django-updates?hl=en.

Reply via email to