Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

[1.3.X] Added protection against spoofing of X_FORWARDED_HOST headers…

…. A security announcement will be made shortly.

Backport of r16758 from trunk.

git-svn-id: http://code.djangoproject.com/svn/django/branches/releases/1.3.X@16761 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
commit 2f7fadc38efa58ac0a8f93f936b82332a199f396 1 parent afe4763
@freakboy3742 freakboy3742 authored
View
2  django/conf/global_settings.py
@@ -399,6 +399,8 @@
DEFAULT_TABLESPACE = ''
DEFAULT_INDEX_TABLESPACE = ''
+USE_X_FORWARDED_HOST = False
+
##############
# MIDDLEWARE #
##############
View
3  django/http/__init__.py
@@ -153,7 +153,8 @@ def __repr__(self):
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']
View
9 docs/ref/request-response.txt
@@ -191,10 +191,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/
View
13 docs/ref/settings.txt
@@ -1960,6 +1960,19 @@ in order to format numbers.
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
View
90 tests/regressiontests/requests/tests.py
@@ -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 @@ def test_httprequest_location(self):
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()
Please sign in to comment.
Something went wrong with that request. Please try again.