+165
-1
lines changedFilter options
+165
-1
lines changed Original file line number Diff line number Diff line change
@@ -155,6 +155,17 @@ def log_message(self, format, *args):
155
155
156
156
sys.stderr.write(msg)
157
157
158
+
def get_environ(self):
159
+
# Strip all headers with underscores in the name before constructing
160
+
# the WSGI environ. This prevents header-spoofing based on ambiguity
161
+
# between underscores and dashes both normalized to underscores in WSGI
162
+
# env vars. Nginx and Apache 2.4+ both do this as well.
163
+
for k, v in self.headers.items():
164
+
if '_' in k:
165
+
del self.headers[k]
166
+
167
+
return super(WSGIRequestHandler, self).get_environ()
168
+
158
169
159
170
def run(addr, port, wsgi_handler, ipv6=False, threading=False):
160
171
server_address = (addr, port)
Original file line number Diff line number Diff line change
@@ -64,6 +64,22 @@ If your authentication mechanism uses a custom HTTP header and not
64
64
class CustomHeaderMiddleware(RemoteUserMiddleware):
65
65
header = 'HTTP_AUTHUSER'
66
66
67
+
.. warning::
68
+
69
+
Be very careful if using a ``RemoteUserMiddleware`` subclass with a custom
70
+
HTTP header. You must be sure that your front-end web server always sets or
71
+
strips that header based on the appropriate authentication checks, never
72
+
permitting an end-user to submit a fake (or "spoofed") header value. Since
73
+
the HTTP headers ``X-Auth-User`` and ``X-Auth_User`` (for example) both
74
+
normalize to the ``HTTP_X_AUTH_USER`` key in ``request.META``, you must
75
+
also check that your web server doesn't allow a spoofed header using
76
+
underscores in place of dashes.
77
+
78
+
This warning doesn't apply to ``RemoteUserMiddleware`` in its default
79
+
configuration with ``header = 'REMOTE_USER'``, since a key that doesn't
80
+
start with ``HTTP_`` in ``request.META`` can only be set by your WSGI
81
+
server, not directly from an HTTP request header.
82
+
67
83
If you need more control, you can create your own authentication backend
68
84
that inherits from :class:`~django.contrib.auth.backends.RemoteUserBackend` and
69
85
override one or more of its attributes and methods.
Original file line number Diff line number Diff line change
@@ -7,6 +7,30 @@ Django 1.4.18 release notes
7
7
Django 1.4.18 fixes several security issues in 1.4.17 as well as a regression
8
8
on Python 2.5 in the 1.4.17 release.
9
9
10
+
WSGI header spoofing via underscore/dash conflation
11
+
===================================================
12
+
13
+
When HTTP headers are placed into the WSGI environ, they are normalized by
14
+
converting to uppercase, converting all dashes to underscores, and prepending
15
+
`HTTP_`. For instance, a header ``X-Auth-User`` would become
16
+
``HTTP_X_AUTH_USER`` in the WSGI environ (and thus also in Django's
17
+
``request.META`` dictionary).
18
+
19
+
Unfortunately, this means that the WSGI environ cannot distinguish between
20
+
headers containing dashes and headers containing underscores: ``X-Auth-User``
21
+
and ``X-Auth_User`` both become ``HTTP_X_AUTH_USER``. This means that if a
22
+
header is used in a security-sensitive way (for instance, passing
23
+
authentication information along from a front-end proxy), even if the proxy
24
+
carefully strips any incoming value for ``X-Auth-User``, an attacker may be
25
+
able to provide an ``X-Auth_User`` header (with underscore) and bypass this
26
+
protection.
27
+
28
+
In order to prevent such attacks, both Nginx and Apache 2.4+ strip all headers
29
+
containing underscores from incoming requests by default. Django's built-in
30
+
development server now does the same. Django's development server is not
31
+
recommended for production use, but matching the behavior of common production
32
+
servers reduces the surface area for behavior changes during deployment.
33
+
10
34
Bugfixes
11
35
========
12
36
Original file line number Diff line number Diff line change
@@ -5,3 +5,27 @@ Django 1.6.10 release notes
5
5
*Under development*
6
6
7
7
Django 1.6.10 fixes several security issues in 1.6.9.
8
+
9
+
WSGI header spoofing via underscore/dash conflation
10
+
===================================================
11
+
12
+
When HTTP headers are placed into the WSGI environ, they are normalized by
13
+
converting to uppercase, converting all dashes to underscores, and prepending
14
+
`HTTP_`. For instance, a header ``X-Auth-User`` would become
15
+
``HTTP_X_AUTH_USER`` in the WSGI environ (and thus also in Django's
16
+
``request.META`` dictionary).
17
+
18
+
Unfortunately, this means that the WSGI environ cannot distinguish between
19
+
headers containing dashes and headers containing underscores: ``X-Auth-User``
20
+
and ``X-Auth_User`` both become ``HTTP_X_AUTH_USER``. This means that if a
21
+
header is used in a security-sensitive way (for instance, passing
22
+
authentication information along from a front-end proxy), even if the proxy
23
+
carefully strips any incoming value for ``X-Auth-User``, an attacker may be
24
+
able to provide an ``X-Auth_User`` header (with underscore) and bypass this
25
+
protection.
26
+
27
+
In order to prevent such attacks, both Nginx and Apache 2.4+ strip all headers
28
+
containing underscores from incoming requests by default. Django's built-in
29
+
development server now does the same. Django's development server is not
30
+
recommended for production use, but matching the behavior of common production
31
+
servers reduces the surface area for behavior changes during deployment.
Original file line number Diff line number Diff line change
@@ -6,7 +6,29 @@ Django 1.7.3 release notes
6
6
7
7
Django 1.7.3 fixes several security issues and bugs in 1.7.2.
8
8
9
-
9
+
WSGI header spoofing via underscore/dash conflation
10
+
===================================================
11
+
12
+
When HTTP headers are placed into the WSGI environ, they are normalized by
13
+
converting to uppercase, converting all dashes to underscores, and prepending
14
+
`HTTP_`. For instance, a header ``X-Auth-User`` would become
15
+
``HTTP_X_AUTH_USER`` in the WSGI environ (and thus also in Django's
16
+
``request.META`` dictionary).
17
+
18
+
Unfortunately, this means that the WSGI environ cannot distinguish between
19
+
headers containing dashes and headers containing underscores: ``X-Auth-User``
20
+
and ``X-Auth_User`` both become ``HTTP_X_AUTH_USER``. This means that if a
21
+
header is used in a security-sensitive way (for instance, passing
22
+
authentication information along from a front-end proxy), even if the proxy
23
+
carefully strips any incoming value for ``X-Auth-User``, an attacker may be
24
+
able to provide an ``X-Auth_User`` header (with underscore) and bypass this
25
+
protection.
26
+
27
+
In order to prevent such attacks, both Nginx and Apache 2.4+ strip all headers
28
+
containing underscores from incoming requests by default. Django's built-in
29
+
development server now does the same. Django's development server is not
30
+
recommended for production use, but matching the behavior of common production
31
+
servers reduces the surface area for behavior changes during deployment.
10
32
11
33
Bugfixes
12
34
========
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
1
+
import sys
2
+
3
+
from django.core.servers.basehttp import WSGIRequestHandler
4
+
from django.test import TestCase
5
+
from django.utils.six import BytesIO, StringIO
6
+
7
+
8
+
class Stub(object):
9
+
def __init__(self, **kwargs):
10
+
self.__dict__.update(kwargs)
11
+
12
+
13
+
class WSGIRequestHandlerTestCase(TestCase):
14
+
15
+
def test_strips_underscore_headers(self):
16
+
"""WSGIRequestHandler ignores headers containing underscores.
17
+
18
+
This follows the lead of nginx and Apache 2.4, and is to avoid
19
+
ambiguity between dashes and underscores in mapping to WSGI environ,
20
+
which can have security implications.
21
+
"""
22
+
def test_app(environ, start_response):
23
+
"""A WSGI app that just reflects its HTTP environ."""
24
+
start_response('200 OK', [])
25
+
http_environ_items = sorted(
26
+
'%s:%s' % (k, v) for k, v in environ.items()
27
+
if k.startswith('HTTP_')
28
+
)
29
+
yield (','.join(http_environ_items)).encode('utf-8')
30
+
31
+
rfile = BytesIO()
32
+
rfile.write(b"GET / HTTP/1.0\r\n")
33
+
rfile.write(b"Some-Header: good\r\n")
34
+
rfile.write(b"Some_Header: bad\r\n")
35
+
rfile.write(b"Other_Header: bad\r\n")
36
+
rfile.seek(0)
37
+
38
+
# WSGIRequestHandler closes the output file; we need to make this a
39
+
# no-op so we can still read its contents.
40
+
class UnclosableBytesIO(BytesIO):
41
+
def close(self):
42
+
pass
43
+
44
+
wfile = UnclosableBytesIO()
45
+
46
+
def makefile(mode, *a, **kw):
47
+
if mode == 'rb':
48
+
return rfile
49
+
elif mode == 'wb':
50
+
return wfile
51
+
52
+
request = Stub(makefile=makefile)
53
+
server = Stub(base_environ={}, get_app=lambda: test_app)
54
+
55
+
# We don't need to check stderr, but we don't want it in test output
56
+
old_stderr = sys.stderr
57
+
sys.stderr = StringIO()
58
+
try:
59
+
# instantiating a handler runs the request as side effect
60
+
WSGIRequestHandler(request, '192.168.0.2', server)
61
+
finally:
62
+
sys.stderr = old_stderr
63
+
64
+
wfile.seek(0)
65
+
body = list(wfile.readlines())[-1]
66
+
67
+
self.assertEqual(body, b'HTTP_SOME_HEADER:good')
You can’t perform that action at this time.
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4