Fixed #5611 -- Restricted accepted content types in parsing POST data
Thanks paulegan for the report and Preston Holmes for the review.
This commit is contained in:
parent
eed4faf16f
commit
dfd4a71751
|
@ -315,7 +315,7 @@ class HttpRequest(object):
|
||||||
self._post_parse_error = True
|
self._post_parse_error = True
|
||||||
|
|
||||||
def _load_post_and_files(self):
|
def _load_post_and_files(self):
|
||||||
# Populates self._post and self._files
|
"""Populate self._post and self._files if the content-type is a form type"""
|
||||||
if self.method != 'POST':
|
if self.method != 'POST':
|
||||||
self._post, self._files = QueryDict('', encoding=self._encoding), MultiValueDict()
|
self._post, self._files = QueryDict('', encoding=self._encoding), MultiValueDict()
|
||||||
return
|
return
|
||||||
|
@ -323,7 +323,7 @@ class HttpRequest(object):
|
||||||
self._mark_post_parse_error()
|
self._mark_post_parse_error()
|
||||||
return
|
return
|
||||||
|
|
||||||
if self.META.get('CONTENT_TYPE', '').startswith('multipart'):
|
if self.META.get('CONTENT_TYPE', '').startswith('multipart/form-data'):
|
||||||
if hasattr(self, '_body'):
|
if hasattr(self, '_body'):
|
||||||
# Use already read data
|
# Use already read data
|
||||||
data = BytesIO(self._body)
|
data = BytesIO(self._body)
|
||||||
|
@ -341,8 +341,10 @@ class HttpRequest(object):
|
||||||
# empty POST
|
# empty POST
|
||||||
self._mark_post_parse_error()
|
self._mark_post_parse_error()
|
||||||
raise
|
raise
|
||||||
else:
|
elif self.META.get('CONTENT_TYPE', '').startswith('application/x-www-form-urlencoded'):
|
||||||
self._post, self._files = QueryDict(self.body, encoding=self._encoding), MultiValueDict()
|
self._post, self._files = QueryDict(self.body, encoding=self._encoding), MultiValueDict()
|
||||||
|
else:
|
||||||
|
self._post, self._files = QueryDict('', encoding=self._encoding), MultiValueDict()
|
||||||
|
|
||||||
## File-like and iterator interface.
|
## File-like and iterator interface.
|
||||||
##
|
##
|
||||||
|
|
|
@ -92,8 +92,14 @@ All attributes should be considered read-only, unless stated otherwise below.
|
||||||
|
|
||||||
.. attribute:: HttpRequest.POST
|
.. attribute:: HttpRequest.POST
|
||||||
|
|
||||||
A dictionary-like object containing all given HTTP POST parameters. See the
|
A dictionary-like object containing all given HTTP POST parameters,
|
||||||
:class:`QueryDict` documentation below.
|
providing that the request contains form data. See the
|
||||||
|
:class:`QueryDict` documentation below. If you need to access raw or
|
||||||
|
non-form data posted in the request, access this through the
|
||||||
|
:attr:`HttpRequest.body` attribute instead.
|
||||||
|
|
||||||
|
.. versionchanged:: 1.5
|
||||||
|
Before Django 1.5, HttpRequest.POST contained non-form data.
|
||||||
|
|
||||||
It's possible that a request can come in via POST with an empty ``POST``
|
It's possible that a request can come in via POST with an empty ``POST``
|
||||||
dictionary -- if, say, a form is requested via the POST HTTP method but
|
dictionary -- if, say, a form is requested via the POST HTTP method but
|
||||||
|
|
|
@ -245,6 +245,18 @@ For consistency with the design of the other generic views,
|
||||||
dictionary into the context, instead passing the variables from the URLconf
|
dictionary into the context, instead passing the variables from the URLconf
|
||||||
directly into the context.
|
directly into the context.
|
||||||
|
|
||||||
|
Non-form data in HTTP requests
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
:attr:`request.POST <django.http.HttpRequest.POST>` will no longer include data
|
||||||
|
posted via HTTP requests with non form-specific content-types in the header.
|
||||||
|
In prior versions, data posted with content-types other than
|
||||||
|
``multipart/form-data`` or ``application/x-www-form-urlencoded`` would still
|
||||||
|
end up represented in the :attr:`request.POST <django.http.HttpRequest.POST>`
|
||||||
|
attribute. Developers wishing to access the raw POST data for these cases,
|
||||||
|
should use the :attr:`request.body <django.http.HttpRequest.body>` attribute
|
||||||
|
instead.
|
||||||
|
|
||||||
OPTIONS, PUT and DELETE requests in the test client
|
OPTIONS, PUT and DELETE requests in the test client
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
|
|
@ -330,6 +330,7 @@ class RequestsTests(unittest.TestCase):
|
||||||
def test_stream(self):
|
def test_stream(self):
|
||||||
payload = b'name=value'
|
payload = b'name=value'
|
||||||
request = WSGIRequest({'REQUEST_METHOD': 'POST',
|
request = WSGIRequest({'REQUEST_METHOD': 'POST',
|
||||||
|
'CONTENT_TYPE': 'application/x-www-form-urlencoded',
|
||||||
'CONTENT_LENGTH': len(payload),
|
'CONTENT_LENGTH': len(payload),
|
||||||
'wsgi.input': BytesIO(payload)})
|
'wsgi.input': BytesIO(payload)})
|
||||||
self.assertEqual(request.read(), b'name=value')
|
self.assertEqual(request.read(), b'name=value')
|
||||||
|
@ -341,6 +342,7 @@ class RequestsTests(unittest.TestCase):
|
||||||
"""
|
"""
|
||||||
payload = b'name=value'
|
payload = b'name=value'
|
||||||
request = WSGIRequest({'REQUEST_METHOD': 'POST',
|
request = WSGIRequest({'REQUEST_METHOD': 'POST',
|
||||||
|
'CONTENT_TYPE': 'application/x-www-form-urlencoded',
|
||||||
'CONTENT_LENGTH': len(payload),
|
'CONTENT_LENGTH': len(payload),
|
||||||
'wsgi.input': BytesIO(payload)})
|
'wsgi.input': BytesIO(payload)})
|
||||||
self.assertEqual(request.POST, {'name': ['value']})
|
self.assertEqual(request.POST, {'name': ['value']})
|
||||||
|
@ -354,6 +356,7 @@ class RequestsTests(unittest.TestCase):
|
||||||
"""
|
"""
|
||||||
payload = b'name=value'
|
payload = b'name=value'
|
||||||
request = WSGIRequest({'REQUEST_METHOD': 'POST',
|
request = WSGIRequest({'REQUEST_METHOD': 'POST',
|
||||||
|
'CONTENT_TYPE': 'application/x-www-form-urlencoded',
|
||||||
'CONTENT_LENGTH': len(payload),
|
'CONTENT_LENGTH': len(payload),
|
||||||
'wsgi.input': BytesIO(payload)})
|
'wsgi.input': BytesIO(payload)})
|
||||||
self.assertEqual(request.read(2), b'na')
|
self.assertEqual(request.read(2), b'na')
|
||||||
|
@ -402,9 +405,28 @@ class RequestsTests(unittest.TestCase):
|
||||||
'wsgi.input': BytesIO(payload)})
|
'wsgi.input': BytesIO(payload)})
|
||||||
self.assertEqual(request.POST, {})
|
self.assertEqual(request.POST, {})
|
||||||
|
|
||||||
|
def test_POST_binary_only(self):
|
||||||
|
payload = b'\r\n\x01\x00\x00\x00ab\x00\x00\xcd\xcc,@'
|
||||||
|
environ = {'REQUEST_METHOD': 'POST',
|
||||||
|
'CONTENT_TYPE': 'application/octet-stream',
|
||||||
|
'CONTENT_LENGTH': len(payload),
|
||||||
|
'wsgi.input': BytesIO(payload)}
|
||||||
|
request = WSGIRequest(environ)
|
||||||
|
self.assertEqual(request.POST, {})
|
||||||
|
self.assertEqual(request.FILES, {})
|
||||||
|
self.assertEqual(request.body, payload)
|
||||||
|
|
||||||
|
# Same test without specifying content-type
|
||||||
|
environ.update({'CONTENT_TYPE': '', 'wsgi.input': BytesIO(payload)})
|
||||||
|
request = WSGIRequest(environ)
|
||||||
|
self.assertEqual(request.POST, {})
|
||||||
|
self.assertEqual(request.FILES, {})
|
||||||
|
self.assertEqual(request.body, payload)
|
||||||
|
|
||||||
def test_read_by_lines(self):
|
def test_read_by_lines(self):
|
||||||
payload = b'name=value'
|
payload = b'name=value'
|
||||||
request = WSGIRequest({'REQUEST_METHOD': 'POST',
|
request = WSGIRequest({'REQUEST_METHOD': 'POST',
|
||||||
|
'CONTENT_TYPE': 'application/x-www-form-urlencoded',
|
||||||
'CONTENT_LENGTH': len(payload),
|
'CONTENT_LENGTH': len(payload),
|
||||||
'wsgi.input': BytesIO(payload)})
|
'wsgi.input': BytesIO(payload)})
|
||||||
self.assertEqual(list(request), [b'name=value'])
|
self.assertEqual(list(request), [b'name=value'])
|
||||||
|
@ -415,6 +437,7 @@ class RequestsTests(unittest.TestCase):
|
||||||
"""
|
"""
|
||||||
payload = b'name=value'
|
payload = b'name=value'
|
||||||
request = WSGIRequest({'REQUEST_METHOD': 'POST',
|
request = WSGIRequest({'REQUEST_METHOD': 'POST',
|
||||||
|
'CONTENT_TYPE': 'application/x-www-form-urlencoded',
|
||||||
'CONTENT_LENGTH': len(payload),
|
'CONTENT_LENGTH': len(payload),
|
||||||
'wsgi.input': BytesIO(payload)})
|
'wsgi.input': BytesIO(payload)})
|
||||||
raw_data = request.body
|
raw_data = request.body
|
||||||
|
@ -427,6 +450,7 @@ class RequestsTests(unittest.TestCase):
|
||||||
"""
|
"""
|
||||||
payload = b'name=value'
|
payload = b'name=value'
|
||||||
request = WSGIRequest({'REQUEST_METHOD': 'POST',
|
request = WSGIRequest({'REQUEST_METHOD': 'POST',
|
||||||
|
'CONTENT_TYPE': 'application/x-www-form-urlencoded',
|
||||||
'CONTENT_LENGTH': len(payload),
|
'CONTENT_LENGTH': len(payload),
|
||||||
'wsgi.input': BytesIO(payload)})
|
'wsgi.input': BytesIO(payload)})
|
||||||
raw_data = request.body
|
raw_data = request.body
|
||||||
|
@ -479,6 +503,7 @@ class RequestsTests(unittest.TestCase):
|
||||||
|
|
||||||
payload = b'name=value'
|
payload = b'name=value'
|
||||||
request = WSGIRequest({'REQUEST_METHOD': 'POST',
|
request = WSGIRequest({'REQUEST_METHOD': 'POST',
|
||||||
|
'CONTENT_TYPE': 'application/x-www-form-urlencoded',
|
||||||
'CONTENT_LENGTH': len(payload),
|
'CONTENT_LENGTH': len(payload),
|
||||||
'wsgi.input': ExplodingBytesIO(payload)})
|
'wsgi.input': ExplodingBytesIO(payload)})
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue