diff options
| author | Natalia <124304+nessita@users.noreply.github.com> | 2026-01-29 22:52:41 -0300 |
|---|---|---|
| committer | Natalia <124304+nessita@users.noreply.github.com> | 2026-03-03 09:16:53 -0300 |
| commit | 4d3c184686626d224d9a87451410ecf802b41f7c (patch) | |
| tree | 36d87c1b19f5c5a579bc4c5f65bd6f1e57d524a7 /django | |
| parent | 94e7f17e0e507a14f30a30f4af2b0213fd9675fc (diff) | |
[5.2.x] Fixed CVE-2026-25673 -- Simplified URLField scheme detection.
This simplicaftion mitigates a potential DoS in URLField on Windows. The
usage of `urlsplit()` in `URLField.to_python()` was replaced with
`str.partition(":")` for URL scheme detection. On Windows, `urlsplit()`
performs Unicode normalization which is slow for certain characters,
making `URLField` vulnerable to DoS via specially crafted POST payloads.
Thanks Seokchan Yoon for the report, and Jake Howard and Shai Berger
for the review.
Refs #36923.
Co-authored-by: Jacob Walls <jacobtylerwalls@gmail.com>
Backport of 951ffb3832cd83ba672c1e3deae2bda128eb9cca from main.
Diffstat (limited to 'django')
| -rw-r--r-- | django/forms/fields.py | 42 |
1 files changed, 16 insertions, 26 deletions
diff --git a/django/forms/fields.py b/django/forms/fields.py index 4bd9c352f2..14e207a257 100644 --- a/django/forms/fields.py +++ b/django/forms/fields.py @@ -13,7 +13,6 @@ import uuid import warnings from decimal import Decimal, DecimalException from io import BytesIO -from urllib.parse import urlsplit, urlunsplit from django.conf import settings from django.core import validators @@ -797,33 +796,24 @@ class URLField(CharField): super().__init__(strip=True, **kwargs) def to_python(self, value): - def split_url(url): - """ - Return a list of url parts via urlsplit(), or raise - ValidationError for some malformed URLs. - """ - try: - return list(urlsplit(url)) - except ValueError: - # urlsplit can raise a ValueError with some - # misformatted URLs. - raise ValidationError(self.error_messages["invalid"], code="invalid") - value = super().to_python(value) if value: - url_fields = split_url(value) - if not url_fields[0]: - # If no URL scheme given, add a scheme. - url_fields[0] = self.assume_scheme - if not url_fields[1]: - # Assume that if no domain is provided, that the path segment - # contains the domain. - url_fields[1] = url_fields[2] - url_fields[2] = "" - # Rebuild the url_fields list, since the domain segment may now - # contain the path too. - url_fields = split_url(urlunsplit(url_fields)) - value = urlunsplit(url_fields) + # Detect scheme via partition to avoid calling urlsplit() on + # potentially large or slow-to-normalize inputs. + scheme, sep, _ = value.partition(":") + if ( + not sep + or not scheme + or not scheme[0].isascii() + or not scheme[0].isalpha() + or "/" in scheme + ): + # No valid scheme found -- prepend the assumed scheme. Handle + # scheme-relative URLs ("//example.com") separately. + if value.startswith("//"): + value = self.assume_scheme + ":" + value + else: + value = self.assume_scheme + "://" + value return value |
