A RetroSearch Logo

Home - News ( United States | United Kingdom | Italy | Germany ) - Football scores

Search Query:

Showing content from https://github.com/django/django/commit/4f6fffc1dc429f1ad428ecf8e6620739e8837450 below:

[1.4.x] Stripped headers containing underscores to prevent spoofing i… · django/django@4f6fffc · GitHub

File tree Expand file treeCollapse file tree 3 files changed

+102

-0

lines changed

Filter options

Expand file treeCollapse file tree 3 files changed

+102

-0

lines changed Original file line number Diff line number Diff line change

@@ -199,6 +199,17 @@ def log_message(self, format, *args):

199 199 200 200

sys.stderr.write(msg)

201 201 202 +

def get_environ(self):

203 +

# Strip all headers with underscores in the name before constructing

204 +

# the WSGI environ. This prevents header-spoofing based on ambiguity

205 +

# between underscores and dashes both normalized to underscores in WSGI

206 +

# env vars. Nginx and Apache 2.4+ both do this as well.

207 +

for k, v in self.headers.items():

208 +

if '_' in k:

209 +

del self.headers[k]

210 + 211 +

return super(WSGIRequestHandler, self).get_environ()

212 + 202 213 203 214

class AdminMediaHandler(handlers.StaticFilesHandler):

204 215

"""

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

@@ -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