diff --git a/django/conf/global_settings.py b/django/conf/global_settings.py index 86f59f0495..b23b53cce1 100644 --- a/django/conf/global_settings.py +++ b/django/conf/global_settings.py @@ -390,6 +390,8 @@ URL_VALIDATOR_USER_AGENT = "Django/%s (http://www.djangoproject.com)" % get_vers DEFAULT_TABLESPACE = '' DEFAULT_INDEX_TABLESPACE = '' +USE_X_FORWARDED_HOST = False + ############## # MIDDLEWARE # ############## diff --git a/django/http/__init__.py b/django/http/__init__.py index b9e4470d87..4a5ba3e39f 100644 --- a/django/http/__init__.py +++ b/django/http/__init__.py @@ -45,7 +45,8 @@ class HttpRequest(object): 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'] diff --git a/docs/ref/request-response.txt b/docs/ref/request-response.txt index fd01cf9138..7837b6bede 100644 --- a/docs/ref/request-response.txt +++ b/docs/ref/request-response.txt @@ -185,10 +185,11 @@ Methods .. 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/ diff --git a/docs/ref/settings.txt b/docs/ref/settings.txt index 33011c08e9..43b56d7844 100644 --- a/docs/ref/settings.txt +++ b/docs/ref/settings.txt @@ -1698,6 +1698,19 @@ and ``NUMBER_GROUPING`` from current locale, to format the number. See also ``THOUSAND_SEPARATOR`` and ``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 diff --git a/tests/regressiontests/requests/tests.py b/tests/regressiontests/requests/tests.py index 556d61e2ce..acae5f479b 100644 --- a/tests/regressiontests/requests/tests.py +++ b/tests/regressiontests/requests/tests.py @@ -2,11 +2,13 @@ from datetime import datetime, timedelta import time import unittest +from django.conf import settings from django.http import HttpRequest, HttpResponse, parse_cookie from django.core.handlers.wsgi import WSGIRequest from django.core.handlers.modpython import ModPythonRequest from django.utils.http import cookie_date + class RequestsTests(unittest.TestCase): def test_httprequest(self): @@ -57,3 +59,91 @@ class RequestsTests(unittest.TestCase): request.path = '' 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