Fixed #32329 -- Made CsrfViewMiddleware catch more specific UnreadablePostError.

Thanks Chris Jerdonek for the review.
This commit is contained in:
Virtosu Bogdan 2021-07-23 12:26:22 +02:00 committed by Mariusz Felisiak
parent 852fa7617e
commit 00ea883ef5
2 changed files with 19 additions and 6 deletions

View File

@ -11,6 +11,7 @@ from urllib.parse import urlparse
from django.conf import settings from django.conf import settings
from django.core.exceptions import DisallowedHost, ImproperlyConfigured from django.core.exceptions import DisallowedHost, ImproperlyConfigured
from django.http import UnreadablePostError
from django.http.request import HttpHeaders from django.http.request import HttpHeaders
from django.urls import get_callable from django.urls import get_callable
from django.utils.cache import patch_vary_headers from django.utils.cache import patch_vary_headers
@ -342,7 +343,7 @@ class CsrfViewMiddleware(MiddlewareMixin):
if request.method == 'POST': if request.method == 'POST':
try: try:
request_csrf_token = request.POST.get('csrfmiddlewaretoken', '') request_csrf_token = request.POST.get('csrfmiddlewaretoken', '')
except OSError: except UnreadablePostError:
# Handle a broken connection before we've completed reading the # Handle a broken connection before we've completed reading the
# POST data. process_view shouldn't raise any exceptions, so # POST data. process_view shouldn't raise any exceptions, so
# we'll ignore and serve the user a 403 (assuming they're still # we'll ignore and serve the user a 403 (assuming they're still

View File

@ -3,7 +3,7 @@ import re
from django.conf import settings from django.conf import settings
from django.contrib.sessions.backends.cache import SessionStore from django.contrib.sessions.backends.cache import SessionStore
from django.core.exceptions import ImproperlyConfigured from django.core.exceptions import ImproperlyConfigured
from django.http import HttpRequest, HttpResponse from django.http import HttpRequest, HttpResponse, UnreadablePostError
from django.middleware.csrf import ( from django.middleware.csrf import (
CSRF_ALLOWED_CHARS, CSRF_SESSION_KEY, CSRF_TOKEN_LENGTH, REASON_BAD_ORIGIN, CSRF_ALLOWED_CHARS, CSRF_SESSION_KEY, CSRF_TOKEN_LENGTH, REASON_BAD_ORIGIN,
REASON_CSRF_TOKEN_MISSING, REASON_NO_CSRF_COOKIE, CsrfViewMiddleware, REASON_CSRF_TOKEN_MISSING, REASON_NO_CSRF_COOKIE, CsrfViewMiddleware,
@ -728,10 +728,10 @@ class CsrfViewMiddlewareTestMixin:
req = self._get_request() req = self._get_request()
ensure_csrf_cookie_view(req) ensure_csrf_cookie_view(req)
def test_post_data_read_failure(self): def test_reading_post_data_raises_unreadable_post_error(self):
""" """
OSErrors during POST data reading are caught and treated as if the An UnreadablePostError raised while reading the POST data should be
POST data wasn't there. handled by the middleware.
""" """
req = self._get_POST_request_with_token() req = self._get_POST_request_with_token()
mw = CsrfViewMiddleware(post_form_view) mw = CsrfViewMiddleware(post_form_view)
@ -740,7 +740,7 @@ class CsrfViewMiddlewareTestMixin:
self.assertIsNone(resp) self.assertIsNone(resp)
req = self._get_POST_request_with_token(request_class=PostErrorRequest) req = self._get_POST_request_with_token(request_class=PostErrorRequest)
req.post_error = OSError('error reading input data') req.post_error = UnreadablePostError('Error reading input data.')
mw.process_request(req) mw.process_request(req)
with self.assertLogs('django.security.csrf', 'WARNING') as cm: with self.assertLogs('django.security.csrf', 'WARNING') as cm:
resp = mw.process_view(req, post_form_view, (), {}) resp = mw.process_view(req, post_form_view, (), {})
@ -750,6 +750,18 @@ class CsrfViewMiddlewareTestMixin:
'Forbidden (%s): ' % REASON_CSRF_TOKEN_MISSING, 'Forbidden (%s): ' % REASON_CSRF_TOKEN_MISSING,
) )
def test_reading_post_data_raises_os_error(self):
"""
An OSError raised while reading the POST data should not be handled by
the middleware.
"""
mw = CsrfViewMiddleware(post_form_view)
req = self._get_POST_request_with_token(request_class=PostErrorRequest)
req.post_error = OSError('Deleted directories/Missing permissions.')
mw.process_request(req)
with self.assertRaises(OSError):
mw.process_view(req, post_form_view, (), {})
@override_settings(ALLOWED_HOSTS=['www.example.com']) @override_settings(ALLOWED_HOSTS=['www.example.com'])
def test_bad_origin_bad_domain(self): def test_bad_origin_bad_domain(self):
"""A request with a bad origin is rejected.""" """A request with a bad origin is rejected."""