Django is one of the most popular web frameworks in Python, powering thousands of web applications worldwide. Whenever a serious security flaw appears in Django, it’s important for all users to pay attention. Today, we’re breaking down CVE-2023-41164 — a vulnerability in Django’s uri_to_iri() function that can let an attacker knock your site offline just by sending a specially crafted, huge Unicode string.
Let's go through how this vulnerability works, see sample code, get the details on how it can be exploited, and learn how to properly patch your site.
What’s the Problem?
Starting in Django 3.2, there’s a utility function called uri_to_iri() in django.utils.encoding. It converts URL-encoded Unicode data to a form that can be used by Django’s internals and database.
The issue: If an attacker sends a URL or request that contains a massive number of certain Unicode characters, uri_to_iri() could use up all your server’s CPU or memory, making the site unusable for real users — a classic Denial of Service (DoS) attack.
Django 4.2 (before 4.2.5)
Fixed versions:
If you’re running Django 3.2.21, 4.1.11, 4.2.5 or higher, you’re safe.
Technical Details
The vulnerable function uri_to_iri() tries to decode any percent-encoded Unicode input into a standard Unicode string. Certain characters (like emojis, symbols, or combined characters) can result in long, slow computations. If an attacker sends a very long string with repeating or complicated Unicode content, Django’s function spends a lot of time and memory trying to decode it, potentially causing a denial of service.
Here’s a quick look at the vulnerable part in simplified form
from urllib.parse import unquote_to_bytes
from django.conf import settings
def uri_to_iri(uri):
if uri is None:
return uri
uri = force_text(uri)
try:
return unquote_to_bytes(uri).decode('utf-8')
except UnicodeDecodeError:
return uri
If uri contains a long string of complex Unicode references, that unquote_to_bytes + .decode('utf-8') section can hang or consume vast amounts of resources.
Let’s see what an attacker could do. They might craft a GET request like this in Python
import requests
# Create a very large, malicious Unicode sequence
evil_unicode = '%F%9F%92%A9' * 50000 # 50,000 URL-encoded emojis
url = f"https://victim-site.com/{evil_unicode}/";
headers = {"User-Agent": "Mozilla/5."}
response = requests.get(url, headers=headers)
print(response.status_code)
What happens?
If the target Django site is using an affected version and processes request paths through uri_to_iri() (common for path processing, routing, or custom middleware), that single request could consume multiple seconds or even minutes of processing time — per request. Enough such requests will quickly hang or “hug of death” your Django app.
You’re running Django 3.2 before 3.2.21, 4.1 before 4.1.11, or 4.2 before 4.2.5
- Your URLs, routing, or any code paths use uri_to_iri() (which is true for many Django installations)
If you allow arbitrary Unicode in URL paths or query parameters (for example, APIs, internationalized routes), you’re at higher risk.
How to Fix
Solution: Upgrade Django.
4.2.5
At the minimum, upgrade to these patch releases. If you’re using an older Django, update as soon as possible.
# Example with pip
pip install "django>=3.2.21,<3.3" # for 3.2 LTS line
# or
pip install "django>=4.1.11,<4.2"
# or
pip install "django>=4.2.5,<4.3"
Other mitigations
- Consider putting limits on input size at your web server/proxy (e.g., using Nginx client_max_uri_length)
References
- Original Django Security Advisory
- MITRE CVE Entry for CVE-2023-41164
- Django Issue Tracker: #35009
- Django Source: django.utils.encoding
Final Thoughts
This bug is a reminder that small details in Unicode and encoding can have outsized impacts, especially in software used by millions. If you’re a Django user, please make sure your frameworks and dependencies are always updated to the latest secure versions. A simple pip install --upgrade django now could save you hours of downtime or a potential security incident later.
Timeline
Published on: 11/03/2023 05:15:29 UTC
Last modified on: 12/14/2023 10:15:07 UTC