Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 2f7fadc

Browse files
committed
[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
1 parent afe4763 commit 2f7fadc

File tree

5 files changed

+112
-5
lines changed

5 files changed

+112
-5
lines changed

django/conf/global_settings.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -399,6 +399,8 @@
399399
DEFAULT_TABLESPACE = ''
400400
DEFAULT_INDEX_TABLESPACE = ''
401401

402+
USE_X_FORWARDED_HOST = False
403+
402404
##############
403405
# MIDDLEWARE #
404406
##############

django/http/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,8 @@ def __repr__(self):
153153
def get_host(self):
154154
"""Returns the HTTP host using the environment or request headers."""
155155
# We try three options, in order of decreasing preference.
156-
if 'HTTP_X_FORWARDED_HOST' in self.META:
156+
if settings.USE_X_FORWARDED_HOST and (
157+
'HTTP_X_FORWARDED_HOST' in self.META):
157158
host = self.META['HTTP_X_FORWARDED_HOST']
158159
elif 'HTTP_HOST' in self.META:
159160
host = self.META['HTTP_HOST']

docs/ref/request-response.txt

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -191,10 +191,11 @@ Methods
191191

192192
.. method:: HttpRequest.get_host()
193193

194-
Returns the originating host of the request using information from the
195-
``HTTP_X_FORWARDED_HOST`` and ``HTTP_HOST`` headers (in that order). If
196-
they don't provide a value, the method uses a combination of
197-
``SERVER_NAME`` and ``SERVER_PORT`` as detailed in `PEP 333`_.
194+
Returns the originating host of the request using information from
195+
the ``HTTP_X_FORWARDED_HOST`` (if enabled in the settings) and ``HTTP_HOST``
196+
headers (in that order). If they don't provide a value, the method
197+
uses a combination of ``SERVER_NAME`` and ``SERVER_PORT`` as
198+
detailed in :pep:`3333`.
198199

199200
.. _PEP 333: http://www.python.org/dev/peps/pep-0333/
200201

docs/ref/settings.txt

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1960,6 +1960,19 @@ in order to format numbers.
19601960

19611961
See also :setting:`THOUSAND_SEPARATOR` and :setting:`NUMBER_GROUPING`.
19621962

1963+
.. setting:: USE_X_FORWARDED_HOST
1964+
1965+
USE_X_FORWARDED_HOST
1966+
--------------------
1967+
1968+
.. versionadded:: 1.3.1
1969+
1970+
Default: ``False``
1971+
1972+
A boolean that specifies whether to use the X-Forwarded-Host header in
1973+
preference to the Host header. This should only be enabled if a proxy
1974+
which sets this header is in use.
1975+
19631976
.. setting:: YEAR_MONTH_FORMAT
19641977

19651978
YEAR_MONTH_FORMAT

tests/regressiontests/requests/tests.py

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,14 @@
22
from datetime import datetime, timedelta
33
from StringIO import StringIO
44

5+
from django.conf import settings
56
from django.core.handlers.modpython import ModPythonRequest
67
from django.core.handlers.wsgi import WSGIRequest, LimitedStream
78
from django.http import HttpRequest, HttpResponse, parse_cookie
89
from django.utils import unittest
910
from django.utils.http import cookie_date
1011

12+
1113
class RequestsTests(unittest.TestCase):
1214
def test_httprequest(self):
1315
request = HttpRequest()
@@ -58,6 +60,94 @@ def test_httprequest_location(self):
5860
self.assertEqual(request.build_absolute_uri(location="/path/with:colons"),
5961
'http://www.example.com/path/with:colons')
6062

63+
def test_http_get_host(self):
64+
old_USE_X_FORWARDED_HOST = settings.USE_X_FORWARDED_HOST
65+
try:
66+
settings.USE_X_FORWARDED_HOST = False
67+
68+
# Check if X_FORWARDED_HOST is provided.
69+
request = HttpRequest()
70+
request.META = {
71+
u'HTTP_X_FORWARDED_HOST': u'forward.com',
72+
u'HTTP_HOST': u'example.com',
73+
u'SERVER_NAME': u'internal.com',
74+
u'SERVER_PORT': 80,
75+
}
76+
# X_FORWARDED_HOST is ignored.
77+
self.assertEqual(request.get_host(), 'example.com')
78+
79+
# Check if X_FORWARDED_HOST isn't provided.
80+
request = HttpRequest()
81+
request.META = {
82+
u'HTTP_HOST': u'example.com',
83+
u'SERVER_NAME': u'internal.com',
84+
u'SERVER_PORT': 80,
85+
}
86+
self.assertEqual(request.get_host(), 'example.com')
87+
88+
# Check if HTTP_HOST isn't provided.
89+
request = HttpRequest()
90+
request.META = {
91+
u'SERVER_NAME': u'internal.com',
92+
u'SERVER_PORT': 80,
93+
}
94+
self.assertEqual(request.get_host(), 'internal.com')
95+
96+
# Check if HTTP_HOST isn't provided, and we're on a nonstandard port
97+
request = HttpRequest()
98+
request.META = {
99+
u'SERVER_NAME': u'internal.com',
100+
u'SERVER_PORT': 8042,
101+
}
102+
self.assertEqual(request.get_host(), 'internal.com:8042')
103+
104+
finally:
105+
settings.USE_X_FORWARDED_HOST = old_USE_X_FORWARDED_HOST
106+
107+
def test_http_get_host_with_x_forwarded_host(self):
108+
old_USE_X_FORWARDED_HOST = settings.USE_X_FORWARDED_HOST
109+
try:
110+
settings.USE_X_FORWARDED_HOST = True
111+
112+
# Check if X_FORWARDED_HOST is provided.
113+
request = HttpRequest()
114+
request.META = {
115+
u'HTTP_X_FORWARDED_HOST': u'forward.com',
116+
u'HTTP_HOST': u'example.com',
117+
u'SERVER_NAME': u'internal.com',
118+
u'SERVER_PORT': 80,
119+
}
120+
# X_FORWARDED_HOST is obeyed.
121+
self.assertEqual(request.get_host(), 'forward.com')
122+
123+
# Check if X_FORWARDED_HOST isn't provided.
124+
request = HttpRequest()
125+
request.META = {
126+
u'HTTP_HOST': u'example.com',
127+
u'SERVER_NAME': u'internal.com',
128+
u'SERVER_PORT': 80,
129+
}
130+
self.assertEqual(request.get_host(), 'example.com')
131+
132+
# Check if HTTP_HOST isn't provided.
133+
request = HttpRequest()
134+
request.META = {
135+
u'SERVER_NAME': u'internal.com',
136+
u'SERVER_PORT': 80,
137+
}
138+
self.assertEqual(request.get_host(), 'internal.com')
139+
140+
# Check if HTTP_HOST isn't provided, and we're on a nonstandard port
141+
request = HttpRequest()
142+
request.META = {
143+
u'SERVER_NAME': u'internal.com',
144+
u'SERVER_PORT': 8042,
145+
}
146+
self.assertEqual(request.get_host(), 'internal.com:8042')
147+
148+
finally:
149+
settings.USE_X_FORWARDED_HOST = old_USE_X_FORWARDED_HOST
150+
61151
def test_near_expiration(self):
62152
"Cookie will expire when an near expiration time is provided"
63153
response = HttpResponse()

0 commit comments

Comments
 (0)