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.