diff options
| author | Jacob Walls <jacobtylerwalls@gmail.com> | 2026-03-24 15:53:27 -0400 |
|---|---|---|
| committer | Sarah Boyce <42296566+sarahboyce@users.noreply.github.com> | 2026-05-05 14:33:27 +0200 |
| commit | 5a89e341bfc77dd67b7fd57b7091b6430558e1f4 (patch) | |
| tree | eda11f2a0d23e74eb8ff6111a44ee2c0d19990fa /django | |
| parent | d75d57c2c06934a65feabd206129b9d1a230a1da (diff) | |
Fixed CVE-2026-5766 -- Enforced DATA_UPLOAD_MAX_MEMORY_SIZE in MemoryFileUploadHandler on ASGI.
In ASGI deployments, Content-Length is not guaranteed to reflect the
actual request body size, so relying on it to gate memory allocation
allowed the limit to be bypassed. The handler now enforces
DATA_UPLOAD_MAX_MEMORY_SIZE regardless of the declared header value.
Thanks to Kyle Agronick for the report. Refs #35289.
Co-authored-by: Natalia <124304+nessita@users.noreply.github.com>
Diffstat (limited to 'django')
| -rw-r--r-- | django/core/files/uploadhandler.py | 21 |
1 files changed, 18 insertions, 3 deletions
diff --git a/django/core/files/uploadhandler.py b/django/core/files/uploadhandler.py index 133c0a597f..e8347b4637 100644 --- a/django/core/files/uploadhandler.py +++ b/django/core/files/uploadhandler.py @@ -3,7 +3,7 @@ Base file upload handler classes, and the built-in concrete subclasses """ import os -from io import BytesIO +from io import BytesIO, UnsupportedOperation from django.conf import settings from django.core.files.uploadedfile import InMemoryUploadedFile, TemporaryUploadedFile @@ -203,9 +203,24 @@ class MemoryFileUploadHandler(FileUploadHandler): Use the content_length to signal whether or not this handler should be used. """ - # Check the content-length header to see if we should # If the post is too large, we cannot use the Memory handler. - self.activated = content_length <= settings.FILE_UPLOAD_MAX_MEMORY_SIZE + # Content-Length can be absent or understated (for example + # `Transfer-Encoding: chunked` on ASGI), so for seekable streams (such + # as SpooledTemporaryFile on ASGI), check the actual size. + + stream = getattr(input_data, "_stream", input_data) + try: + content_length = stream.seek(0, os.SEEK_END) + except (UnsupportedOperation, AttributeError): + # Cannot seek; fall back to the Content-Length parameter. + # On WSGI the stream enforces this value so it is trustworthy. + pass + else: + stream.seek(0) + self.activated = ( + content_length is not None + and content_length <= settings.FILE_UPLOAD_MAX_MEMORY_SIZE + ) def new_file(self, *args, **kwargs): super().new_file(*args, **kwargs) |
