VDB
Sign up
MEDIUM5.3

GHSA-2m8g-3cmr-wg3w

Django REST framework: Potential bypass of Django `DATA_UPLOAD_MAX_MEMORY_SIZE` when parsing oversized JSON and urlencoded request bodies via DRF `request.data`

Quick fix

GHSA-2m8g-3cmr-wg3w — djangorestframework: upgrade to the fixed version with the command below.

pip install --upgrade 'djangorestframework>=3.17.2'

Details

## Summary

While investigating Django REST Framework's request parsing behavior, I identified that DRF's high-level `request.data` parsing appears to bypass Django's configured `DATA_UPLOAD_MAX_MEMORY_SIZE` protection for `application/json` and `application/x-www-form-urlencoded` request bodies.

In the tested configurations, Django correctly raises `RequestDataTooBig` when applications access `request.body` or Django's native `request.POST`, but DRF successfully parses the same oversized payloads through `request.data`.

This behavior appears to occur because DRF passes the underlying `HttpRequest` object directly to parsers, which consume the request stream through Django's lower-level streaming interface rather than the guarded `request.body` path.

I am reporting this privately because I am unsure whether this behavior is considered part of DRF's intended security boundary, but it appears to bypass a documented Django request-size protection for common DRF request parsing paths and may have availability implications.

# What I Verified

I verified the behavior locally using the following combinations:

* Django **6.0.7** + DRF **3.17.1** → **Affected** * Django **6.0.7** + DRF **current upstream main** → **Affected**

For both versions, the observed behavior was:

``` Django request.body → RequestDataTooBig

Django request.POST (application/x-www-form-urlencoded) → RequestDataTooBig

Django request.read() → Reads the entire oversized request body

DRF request.data → Successfully parses oversized JSON and urlencoded request bodies ```

I also confirmed that:

* `multipart/form-data` remains protected because DRF delegates multipart parsing to Django's multipart parser. * The behavior reproduces on both direct WSGI and ASGI servers without a reverse proxy or external request-size middleware.

# Technical Details

The relevant execution flow is:

``` APIView

rest_framework.request.Request

request.data

Request._load_data_and_files()

Request._parse()

Request._load_stream()

self._stream = self._request

JSONParser.parse(...) or FormParser.parse(...)

stream.read() / json.load(...) ```

The important implementation detail is that DRF assigns the original Django `HttpRequest` object as the parser stream.

Unlike `request.body` and Django's native form parsing, consuming the stream through `HttpRequest.read()` does not trigger Django's `RequestDataTooBig` protection.

As a result, DRF's built-in parsers successfully consume oversized request bodies that Django itself would reject through its higher-level request interfaces.

# Reproduction Steps

## Environment

Python 3.13

Django 6.0.7

Django REST Framework 3.17.1 (also reproduced on current upstream main)

Configure:

```python DATA_UPLOAD_MAX_MEMORY_SIZE = 10 ```

Create a simple DRF API view:

```python from rest_framework.views import APIView from rest_framework.response import Response

class DemoView(APIView): def post(self, request): return Response(request.data) ```

Start the application.

Send an oversized JSON request:

``` POST /demo Content-Type: application/json Content-Length: >10 bytes ```

Example:

```json { "value": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA..." } ```

Observed:

``` HTTP 200

JSON successfully parsed ```

Now compare against:

```python request.body ```

Observed:

``` RequestDataTooBig ```

Likewise, compare against:

```python request.POST ```

using

``` application/x-www-form-urlencoded ```

Observed:

``` RequestDataTooBig ```

This demonstrates different enforcement depending on which request API is used.

# Root Cause

Django documents `HttpRequest.read()` as a streaming interface.

DRF exposes `request.data` as the primary high-level request parsing API.

Currently, DRF forwards the raw Django request stream directly to parsers before any request-size validation equivalent to Django's `request.body` path occurs.

Consequently:

* JSONParser * FormParser

fully consume oversized request bodies despite Django's configured request-size limit.

# Security Impact

This does **not** appear to introduce:

* Authentication bypass * Authorization bypass * Remote code execution * Information disclosure * Integrity compromise

However, it may reduce the effectiveness of deployments relying on Django's `DATA_UPLOAD_MAX_MEMORY_SIZE` to limit request-body resource consumption.

Potential consequences include:

* Additional memory allocation during JSON parsing * Additional CPU usage while decoding large JSON payloads * Increased resource consumption when handling oversized request bodies * Reduced effectiveness of Django's configured request-size protection for DRF endpoints using `request.data`

The practical impact depends on deployment configuration, including:

* upstream request-size limits * reverse proxy configuration * authentication * rate limiting * endpoint exposure

# Memory Observations

During local testing I observed successful parsing of oversized request bodies despite the configured limit.

Representative measurements showed significantly increased memory allocation while parsing large JSON and urlencoded payloads.

I intentionally did **not** perform destructive concurrency testing or attempt to exhaust system resources.

# Scope

Confirmed affected:

* application/json * application/x-www-form-urlencoded

Confirmed not affected:

* multipart/form-data

# Suggested Fix Direction

One possible approach would be for DRF to enforce Django's configured `DATA_UPLOAD_MAX_MEMORY_SIZE` before handing the raw request stream to parsers that fully materialize request bodies in memory.

This would preserve Django's configured request-size protection for the common `request.data` API without requiring broader changes to Django's documented streaming interface.

# Versions Tested

Affected:

* Django 6.0.7 + DRF 3.17.1 * Django 6.0.7 + DRF current upstream main

I did not perform a complete historical version bisect.

# Disclosure

I have not publicly disclosed this behavior.

I am submitting it privately in accordance with the project's security policy because I am unsure whether maintainers consider this part of DRF's intended security boundary.

# Note:

**Thank you for taking the time to review this report.**

If you determine that this behavior should be addressed, I would be happy to help investigate further, develop a fix, add regression tests, and submit a patch if you'd find that helpful.

I have experience as a **Python/Django software engineer, security researcher, and open-source contributor**, and I'd be glad to contribute if you think that would be useful.

Are you affected?

Enter the version of the package you're using.

Affected packages

PyPI/djangorestframework
Introduced in: 0Fixed in: 3.17.2
Fixpip install --upgrade 'djangorestframework>=3.17.2'

References