1
0
mirror of https://github.com/django/django.git synced 2025-06-05 03:29:12 +00:00

Fixed #34709 -- Raised BadRequest for non-UTF-8 requests with the application/x-www-form-urlencoded content type.

Thanks Eki Xu for the report.
This commit is contained in:
Mariusz Felisiak 2023-08-25 21:27:22 +02:00 committed by GitHub
parent 9c37103a98
commit 11920e7795
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 57 additions and 13 deletions

View File

@ -7,6 +7,7 @@ from urllib.parse import parse_qsl, quote, urlencode, urljoin, urlsplit
from django.conf import settings from django.conf import settings
from django.core import signing from django.core import signing
from django.core.exceptions import ( from django.core.exceptions import (
BadRequest,
DisallowedHost, DisallowedHost,
ImproperlyConfigured, ImproperlyConfigured,
RequestDataTooBig, RequestDataTooBig,
@ -377,10 +378,16 @@ class HttpRequest:
self._mark_post_parse_error() self._mark_post_parse_error()
raise raise
elif self.content_type == "application/x-www-form-urlencoded": elif self.content_type == "application/x-www-form-urlencoded":
self._post, self._files = ( # According to RFC 1866, the "application/x-www-form-urlencoded"
QueryDict(self.body, encoding=self._encoding), # content type does not have a charset and should be always treated
MultiValueDict(), # as UTF-8.
) if self._encoding is not None and self._encoding.lower() != "utf-8":
raise BadRequest(
"HTTP requests with the 'application/x-www-form-urlencoded' "
"content type must be UTF-8 encoded."
)
self._post = QueryDict(self.body, encoding="utf-8")
self._files = MultiValueDict()
else: else:
self._post, self._files = ( self._post, self._files = (
QueryDict(encoding=self._encoding), QueryDict(encoding=self._encoding),

View File

@ -580,6 +580,10 @@ Miscellaneous
* Executing SQL queries before the app registry has been fully populated now * Executing SQL queries before the app registry has been fully populated now
raises :exc:`RuntimeWarning`. raises :exc:`RuntimeWarning`.
* :exc:`~django.core.exceptions.BadRequest` is raised for non-UTF-8 encoded
requests with the :mimetype:`application/x-www-form-urlencoded` content type.
See :rfc:`1866` for more details.
.. _deprecated-features-5.0: .. _deprecated-features-5.0:
Features deprecated in 5.0 Features deprecated in 5.0

View File

@ -3,7 +3,7 @@ from io import BytesIO
from itertools import chain from itertools import chain
from urllib.parse import urlencode from urllib.parse import urlencode
from django.core.exceptions import DisallowedHost from django.core.exceptions import BadRequest, DisallowedHost
from django.core.handlers.wsgi import LimitedStream, WSGIRequest from django.core.handlers.wsgi import LimitedStream, WSGIRequest
from django.http import ( from django.http import (
HttpHeaders, HttpHeaders,
@ -369,10 +369,7 @@ class RequestsTests(SimpleTestCase):
) )
self.assertEqual(request.POST, {"key": ["España"]}) self.assertEqual(request.POST, {"key": ["España"]})
def test_alternate_charset_POST(self): def test_non_utf8_charset_POST_bad_request(self):
"""
Test a POST with non-utf-8 payload encoding.
"""
payload = FakePayload(urlencode({"key": "España".encode("latin-1")})) payload = FakePayload(urlencode({"key": "España".encode("latin-1")}))
request = WSGIRequest( request = WSGIRequest(
{ {
@ -382,7 +379,30 @@ class RequestsTests(SimpleTestCase):
"wsgi.input": payload, "wsgi.input": payload,
} }
) )
self.assertEqual(request.POST, {"key": ["España"]}) msg = (
"HTTP requests with the 'application/x-www-form-urlencoded' content type "
"must be UTF-8 encoded."
)
with self.assertRaisesMessage(BadRequest, msg):
request.POST
with self.assertRaisesMessage(BadRequest, msg):
request.FILES
def test_utf8_charset_POST(self):
for charset in ["utf-8", "UTF-8"]:
with self.subTest(charset=charset):
payload = FakePayload(urlencode({"key": "España"}))
request = WSGIRequest(
{
"REQUEST_METHOD": "POST",
"CONTENT_LENGTH": len(payload),
"CONTENT_TYPE": (
f"application/x-www-form-urlencoded; charset={charset}"
),
"wsgi.input": payload,
}
)
self.assertEqual(request.POST, {"key": ["España"]})
def test_body_after_POST_multipart_form_data(self): def test_body_after_POST_multipart_form_data(self):
""" """
@ -694,18 +714,31 @@ class RequestsTests(SimpleTestCase):
request.body request.body
def test_set_encoding_clears_POST(self): def test_set_encoding_clears_POST(self):
payload = FakePayload("name=Hello Günter") payload = FakePayload(
"\r\n".join(
[
f"--{BOUNDARY}",
'Content-Disposition: form-data; name="name"',
"",
"Hello Günter",
f"--{BOUNDARY}--",
"",
]
)
)
request = WSGIRequest( request = WSGIRequest(
{ {
"REQUEST_METHOD": "POST", "REQUEST_METHOD": "POST",
"CONTENT_TYPE": "application/x-www-form-urlencoded", "CONTENT_TYPE": MULTIPART_CONTENT,
"CONTENT_LENGTH": len(payload), "CONTENT_LENGTH": len(payload),
"wsgi.input": payload, "wsgi.input": payload,
} }
) )
self.assertEqual(request.POST, {"name": ["Hello Günter"]}) self.assertEqual(request.POST, {"name": ["Hello Günter"]})
request.encoding = "iso-8859-16" request.encoding = "iso-8859-16"
self.assertEqual(request.POST, {"name": ["Hello GĂŒnter"]}) # FIXME: POST should be accessible after changing the encoding
# (refs #14035).
# self.assertEqual(request.POST, {"name": ["Hello GĂŒnter"]})
def test_set_encoding_clears_GET(self): def test_set_encoding_clears_GET(self):
payload = FakePayload("") payload = FakePayload("")