Django's DATA_UPLOAD_MAX_MEMORY_SIZE setting is designed to limit how much request-body data is loaded into memory.
While investigating request parsing behavior in Django REST Framework (DRF), I found that this protection was not consistently enforced when applications accessed request data through DRF's high-level request.data API.
The issue was reported responsibly and was later published as CVE-2026-73228.
In this article, I'll walk through the behavior I observed, why it happened, its security impact, and the broader engineering lesson behind the vulnerability.
CVE: CVE-2026-73228
Project: Django REST Framework
Severity: Moderate (CVSS 3.1: 5.3)
Weaknesses: CWE-400, CWE-770
Affected: Django REST Framework <= 3.17.1
Fixed: Django REST Framework 3.17.2
Reporter: Zain Nadeem
The Security Boundary I Was Investigating
Django provides the DATA_UPLOAD_MAX_MEMORY_SIZE setting to limit the size of request data that may be loaded into memory.
While testing how this protection behaved across different request APIs, I noticed an important difference.
With an intentionally small configured limit, accessing an oversized request through Django's guarded APIs behaved as expected:
Django request.body
↓
RequestDataTooBig
For URL-encoded form data, Django's native request.POST path also enforced the configured limit.
But the equivalent request handled through Django REST Framework behaved differently:
DRF request.data
↓
Oversized request successfully parsed
That inconsistency was the starting point of the investigation.
Reproducing the Behavior
I verified the issue using:
- Django 6.0.7
- Django REST Framework 3.17.1
- Django REST Framework's then-current upstream main branch
A minimal DRF endpoint was enough to expose the behavior:
from rest_framework.views import APIView
from rest_framework.response import Response
class DemoView(APIView):
def post(self, request):
return Response(request.data)
With a deliberately small request-size limit configured, an oversized JSON request could still be parsed through:
request.data
while accessing the same oversized body through:
request.body
resulted in Django raising:
RequestDataTooBig
The important finding wasn't simply that a large request could be sent.
It was that the same configured protection produced different results depending on the request parsing path used by the application.
Following the Parsing Path
The next step was tracing how DRF turns an incoming Django request into request.data.
At a high level, the relevant flow was:
APIView
↓
rest_framework.request.Request
↓
request.data
↓
Request._load_data_and_files()
↓
Request._parse()
↓
Request._load_stream()
↓
underlying Django HttpRequest
↓
JSONParser / FormParser
↓
stream consumption
This revealed the important implementation detail.
DRF could provide the underlying Django HttpRequest as the stream consumed by its parser.
For JSON and URL-encoded request bodies, the parsing path could therefore consume the request through the lower-level stream interface rather than first going through the guarded request.body behavior.
That distinction matters because Django's request interfaces do not all enforce the memory-size protection at exactly the same point.
Why This Became a Security Issue
Developers using Django REST Framework normally interact with:
request.data
It is DRF's standard high-level abstraction for parsed request content.
A deployment could therefore configure Django's request-size protection and reasonably expect oversized request bodies to be rejected, while DRF endpoints using request.data could still parse those bodies.
The issue affected the built-in parsing paths for:
application/jsonapplication/x-www-form-urlencoded
During my testing, multipart/form-data did not exhibit the same behavior because DRF delegates multipart handling through Django's multipart parsing machinery.
Security Impact
This vulnerability was not an authentication or authorization bypass.
It did not provide:
- remote code execution
- information disclosure
- privilege escalation
- authentication bypass
- integrity compromise
The security concern was resource consumption.
If an application relied on Django's DATA_UPLOAD_MAX_MEMORY_SIZE as part of its request-body resource controls, oversized requests reaching affected DRF parsing paths could consume additional memory and CPU during parsing.
The practical impact depends heavily on the deployment.
Important factors include:
- reverse-proxy request limits
- upstream server configuration
- endpoint exposure
- authentication requirements
- rate limiting
- infrastructure-level request controls
For that reason, the vulnerability was classified as an availability issue rather than something like code execution or data compromise.
The published advisory assigns:
CVSS 3.1: 5.3 (Moderate)
AV:N / AC:L / PR:N / UI:N / S:U / C:N / I:N / A:L
It is also associated with:
- CWE-400 — Uncontrolled Resource Consumption
- CWE-770 — Allocation of Resources Without Limits or Throttling
Root Cause
The root cause came down to a difference in abstraction boundaries.
Django had a configured protection.
DRF provided a higher-level request parsing abstraction.
But the parser could consume the underlying request stream through a path where the expected size validation had not yet occurred.
Conceptually:
Expected:
Incoming request
↓
Size validation
↓
Parser
↓
request.data
The affected path was effectively closer to:
Incoming request
↓
Underlying request stream
↓
DRF parser
↓
request.data
That difference allowed parsers which fully materialize request content to process oversized bodies despite the Django configuration.
This is a useful example of a broader security principle:
A security control implemented at one abstraction layer is only effective if higher-level abstractions cannot unintentionally route around it.
The Fix
The issue was addressed upstream and is fixed in Django REST Framework 3.17.2.
The remediation ensures that Django's configured request-size protection is respected before affected DRF parsing paths consume oversized request bodies.
Applications using affected versions should therefore upgrade to:
Django REST Framework >= 3.17.2
Infrastructure-level request limits remain valuable as defense in depth, but they should complement framework-level protections rather than replace them.
What I Learned From This Research
One of the most interesting parts of this vulnerability was that the individual components were behaving consistently with their own interfaces.
The security problem appeared at the boundary between them.
A few lessons stand out.
1. Test security controls through the framework API developers actually use
Testing only:
request.body
would not have exposed this behavior.
For DRF applications, testing:
request.data
was essential.
2. Trace abstractions all the way down
High-level framework APIs often hide several layers of request processing.
When security behavior differs unexpectedly, following the complete call path can reveal where enforcement is being skipped.
3. Compare equivalent inputs across different interfaces
A particularly useful technique in this investigation was keeping the request essentially the same while changing only the API used to consume it.
That made the enforcement difference much easier to isolate.
4. Availability protections deserve security review too
Request-size limits may look like ordinary configuration, but they can form an important part of an application's resource-exhaustion defenses.
Responsible Disclosure
I reported the issue privately through the project's security process before public disclosure.
The investigation, upstream review, remediation, testing, and eventual advisory publication resulted in:
CVE-2026-73228
I intentionally avoided destructive resource-exhaustion testing during the research. The goal was to demonstrate the enforcement inconsistency and establish its impact without attempting to exhaust production or shared infrastructure.
References
Full technical case study:
https://zainnadeem786.github.io/research/cve-2026-73228.html
Official GitHub Security Advisory:
https://github.com/encode/django-rest-framework/security/advisories/GHSA-2m8g-3cmr-wg3w
Upstream remediation PR:
https://github.com/encode/django-rest-framework/pull/10013
Django REST Framework:
https://github.com/encode/django-rest-framework
Security research often starts with something small: two APIs that appear to provide equivalent behavior but don't.
In this case, following that inconsistency through the framework's parsing stack uncovered a security boundary worth fixing.
If you work with Django or Django REST Framework, I hope this analysis is useful when reviewing request parsing, resource limits, and framework-level security assumptions.
Top comments (0)