A high-level parser crossed a lower-level trust boundary.
Django provides DATA_UPLOAD_MAX_MEMORY_SIZE as a limit on request data that is materialized in memory. During testing, Django correctly raised RequestDataTooBig when an oversized body was accessed through request.body, and its native form handling enforced the same policy. The same payload could still be parsed through DRF’s high-level request.data interface for JSON and URL-encoded form bodies.
The difference mattered because DRF handed its parsers the underlying Django request stream. Reading that stream directly did not pass through the protected request.body property, so a safeguard configured by the application was not consistently applied.
The result depended on which request interface performed the read.
| Path | Oversized request result | Scope |
|---|---|---|
Django request.body | RequestDataTooBig | Protected |
Django request.POST | RequestDataTooBig | URL-encoded form data |
DRF request.data | Body parsed | JSON and URL-encoded data |
| DRF multipart parser | Django behavior retained | Not affected by this path |
The public record lists DRF versions through 3.17.1 as affected and versions later than 3.17.1 as patched. The finding was reproduced without relying on a reverse proxy or external request-size middleware, which isolated the framework behavior from infrastructure controls.
Built-in parsers consumed the original stream directly.
DRF’s request wrapper loaded the underlying stream and passed it to the selected parser. For the built-in JSON and form parsers, calls such as stream.read() or json.load(...) could therefore consume the request before Django’s guarded body property was involved.
# Simplified affected flow
stream = self._request
parsed = parser.parse(stream, media_type, parser_context)
# The parser reads the underlying stream directly;
# Django's request.body size check is not reached.
This was a policy-enforcement mismatch rather than a failure of the Django setting itself: the setting worked when execution used the Django interfaces designed to enforce it.
Practical risk depends on surrounding deployment controls.
An exposed DRF endpoint that parses attacker-controlled JSON or URL-encoded bodies could allocate memory and spend CPU parsing a payload larger than the application’s configured Django limit. The practical effect varies with upstream body limits, authentication, throttling, endpoint exposure, worker configuration, and process-level resource controls.
- Confirmed affected content types:
application/jsonandapplication/x-www-form-urlencoded. - Multipart handling remained delegated to Django’s multipart parser.
- Reverse proxies and gateways can provide an independent outer request-size boundary, but they do not make inconsistent application-level enforcement desirable.
- The testing focused on controlled parsing behavior and did not attempt destructive concurrency or resource-exhaustion testing.
Route materializing parsers through Django’s guarded body.
The public remediation changes the built-in JSONParser and FormParser path to read self.body and wrap the cached bytes in io.BytesIO. Accessing self.body lets Django enforce DATA_UPLOAD_MAX_MEMORY_SIZE before the parser materializes the content.
if isinstance(parser, (JSONParser, FormParser)):
stream = io.BytesIO(self.body)
Regression coverage verifies rejection for oversized JSON and URL-encoded bodies, continued parsing for small bodies, unchanged multipart uploads, and retention of the streaming path for custom parsers.
Compare two request APIs with a deliberately tiny limit.
A bounded local test can set an intentionally small limit and send a small payload that exceeds it. This demonstrates the inconsistent code path without stressing the host.
# settings.py — local test only
DATA_UPLOAD_MAX_MEMORY_SIZE = 10
# Minimal DRF view
class DemoView(APIView):
def post(self, request):
return Response(request.data)
On an affected version, a JSON body just over ten bytes could be returned successfully through request.data, while accessing the same request through request.body raised RequestDataTooBig. Run any reproduction only in an isolated development environment you control.
Reporter credit is explicit; public PR authorship is treated separately.
The official GitHub advisory credits zainnadeem786 as reporter. The report documented the divergent parsing behavior, affected content types, execution flow, bounded reproduction, and a possible fix direction.
Public PR #10013 is linked here as related upstream remediation. Because the public backport page is presented under a maintainer account, this article does not use that page alone to claim public PR authorship.
Framework abstractions should preserve host-level safeguards.
- High-level parsing APIs should apply the resource policies developers reasonably expect from the underlying framework.
- Materializing parsers and intentionally streaming custom parsers need distinct, explicit behavior.
- Request-policy regression tests should cover each built-in content type, not only the most common JSON path.
- Proxy limits are useful defense in depth, but application controls should still behave consistently without them.