DEV Community

Cover image for CVE-2026-73228: Request Parsing Size-Limit Bypass in Django REST Framework
Zain Nadeem
Zain Nadeem

Posted on Originally published at zainnadeem786.github.io

CVE-2026-73228: Request Parsing Size-Limit Bypass in Django REST Framework

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

With a deliberately small request-size limit configured, an oversized JSON request could still be parsed through:

request.data
Enter fullscreen mode Exit fullscreen mode

while accessing the same oversized body through:

request.body
Enter fullscreen mode Exit fullscreen mode

resulted in Django raising:

RequestDataTooBig
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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/json
  • application/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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

The affected path was effectively closer to:

Incoming request
      ↓
Underlying request stream
      ↓
DRF parser
      ↓
request.data
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

would not have exposed this behavior.

For DRF applications, testing:

request.data
Enter fullscreen mode Exit fullscreen mode

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)