VYPR
Moderate severityNVD Advisory· Published Apr 2, 2025· Updated Apr 2, 2025

CVE-2025-27556

CVE-2025-27556

Description

An issue was discovered in Django 5.1 before 5.1.8 and 5.0 before 5.0.14. The NFKC normalization is slow on Windows. As a consequence, django.contrib.auth.views.LoginView, django.contrib.auth.views.LogoutView, and django.views.i18n.set_language are subject to a potential denial-of-service attack via certain inputs with a very large number of Unicode characters.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
DjangoPyPI
>= 5.0, < 5.0.145.0.14
DjangoPyPI
>= 5.1, < 5.1.85.1.8

Affected products

1

Patches

4
39e2297210d9

Fixed CVE-2025-27556 -- Mitigated potential DoS in url_has_allowed_host_and_scheme() on Windows.

https://github.com/django/djangoSarah BoyceMar 6, 2025via ghsa
6 files changed · +44 4
  • django/core/validators.py+2 1 modified
    @@ -6,6 +6,7 @@
     
     from django.core.exceptions import ValidationError
     from django.utils.deconstruct import deconstructible
    +from django.utils.http import MAX_URL_LENGTH
     from django.utils.ipv6 import is_valid_ipv6_address
     from django.utils.regex_helper import _lazy_re_compile
     from django.utils.translation import gettext_lazy as _
    @@ -152,7 +153,7 @@ class URLValidator(RegexValidator):
         message = _("Enter a valid URL.")
         schemes = ["http", "https", "ftp", "ftps"]
         unsafe_chars = frozenset("\t\r\n")
    -    max_length = 2048
    +    max_length = MAX_URL_LENGTH
     
         def __init__(self, schemes=None, **kwargs):
             super().__init__(**kwargs)
    
  • django/utils/html.py+1 2 modified
    @@ -13,7 +13,7 @@
     from django.core.validators import EmailValidator
     from django.utils.deprecation import RemovedInDjango70Warning
     from django.utils.functional import Promise, cached_property, keep_lazy, keep_lazy_text
    -from django.utils.http import RFC3986_GENDELIMS, RFC3986_SUBDELIMS
    +from django.utils.http import MAX_URL_LENGTH, RFC3986_GENDELIMS, RFC3986_SUBDELIMS
     from django.utils.regex_helper import _lazy_re_compile
     from django.utils.safestring import SafeData, SafeString, mark_safe
     from django.utils.text import normalize_newlines
    @@ -41,7 +41,6 @@
         )
     )
     
    -MAX_URL_LENGTH = 2048
     MAX_STRIP_TAGS_DEPTH = 50
     
     
    
  • django/utils/http.py+5 1 modified
    @@ -39,6 +39,7 @@
     
     RFC3986_GENDELIMS = ":/?#[]@"
     RFC3986_SUBDELIMS = "!$&'()*+,;="
    +MAX_URL_LENGTH = 2048
     
     
     def urlencode(query, doseq=False):
    @@ -274,7 +275,10 @@ def url_has_allowed_host_and_scheme(url, allowed_hosts, require_https=False):
     def _url_has_allowed_host_and_scheme(url, allowed_hosts, require_https=False):
         # Chrome considers any URL with more than two slashes to be absolute, but
         # urlsplit is not so flexible. Treat any url with three slashes as unsafe.
    -    if url.startswith("///"):
    +    if url.startswith("///") or len(url) > MAX_URL_LENGTH:
    +        # urlsplit does not perform validation of inputs. Unicode normalization
    +        # is very slow on Windows and can be a DoS attack vector.
    +        # https://docs.python.org/3/library/urllib.parse.html#url-parsing-security
             return False
         try:
             url_info = urlsplit(url)
    
  • docs/releases/5.0.14.txt+10 0 modified
    @@ -5,3 +5,13 @@ Django 5.0.14 release notes
     *April 2, 2025*
     
     Django 5.0.14 fixes a security issue with severity "moderate" in 5.0.13.
    +
    +CVE-2025-27556: Potential denial-of-service vulnerability in ``LoginView``, ``LogoutView``, and ``set_language()`` on Windows
    +=============================================================================================================================
    +
    +Python's :func:`NFKC normalization <python:unicodedata.normalize>` is slow on
    +Windows. As a consequence, :class:`~django.contrib.auth.views.LoginView`,
    +:class:`~django.contrib.auth.views.LogoutView`, and
    +:func:`~django.views.i18n.set_language` were subject to a potential
    +denial-of-service attack via certain inputs with a very large number of Unicode
    +characters.
    
  • docs/releases/5.1.8.txt+10 0 modified
    @@ -7,6 +7,16 @@ Django 5.1.8 release notes
     Django 5.1.8 fixes a security issue with severity "moderate" and several bugs
     in 5.1.7.
     
    +CVE-2025-27556: Potential denial-of-service vulnerability in ``LoginView``, ``LogoutView``, and ``set_language()`` on Windows
    +=============================================================================================================================
    +
    +Python's :func:`NFKC normalization <python:unicodedata.normalize>` is slow on
    +Windows. As a consequence, :class:`~django.contrib.auth.views.LoginView`,
    +:class:`~django.contrib.auth.views.LogoutView`, and
    +:func:`~django.views.i18n.set_language` were subject to a potential
    +denial-of-service attack via certain inputs with a very large number of Unicode
    +characters.
    +
     Bugfixes
     ========
     
    
  • tests/utils_tests/test_http.py+16 0 modified
    @@ -7,6 +7,7 @@
     from django.utils.datastructures import MultiValueDict
     from django.utils.http import (
         MAX_HEADER_LENGTH,
    +    MAX_URL_LENGTH,
         base36_to_int,
         content_disposition_header,
         escape_leading_slashes,
    @@ -274,6 +275,21 @@ def test_secure_param_non_https_urls(self):
                         False,
                     )
     
    +    def test_max_url_length(self):
    +        allowed_host = "example.com"
    +        max_extra_characters = "é" * (MAX_URL_LENGTH - len(allowed_host) - 1)
    +        max_length_boundary_url = f"{allowed_host}/{max_extra_characters}"
    +        cases = [
    +            (max_length_boundary_url, True),
    +            (max_length_boundary_url + "ú", False),
    +        ]
    +        for url, expected in cases:
    +            with self.subTest(url=url):
    +                self.assertIs(
    +                    url_has_allowed_host_and_scheme(url, allowed_hosts={allowed_host}),
    +                    expected,
    +                )
    +
     
     class URLSafeBase64Tests(unittest.TestCase):
         def test_roundtrip(self):
    
2cb311f7b069

[5.2.x] Fixed CVE-2025-27556 -- Mitigated potential DoS in url_has_allowed_host_and_scheme() on Windows.

https://github.com/django/djangoSarah BoyceMar 6, 2025via ghsa
6 files changed · +44 4
  • django/core/validators.py+2 1 modified
    @@ -6,6 +6,7 @@
     
     from django.core.exceptions import ValidationError
     from django.utils.deconstruct import deconstructible
    +from django.utils.http import MAX_URL_LENGTH
     from django.utils.ipv6 import is_valid_ipv6_address
     from django.utils.regex_helper import _lazy_re_compile
     from django.utils.translation import gettext_lazy as _
    @@ -152,7 +153,7 @@ class URLValidator(RegexValidator):
         message = _("Enter a valid URL.")
         schemes = ["http", "https", "ftp", "ftps"]
         unsafe_chars = frozenset("\t\r\n")
    -    max_length = 2048
    +    max_length = MAX_URL_LENGTH
     
         def __init__(self, schemes=None, **kwargs):
             super().__init__(**kwargs)
    
  • django/utils/html.py+1 2 modified
    @@ -12,7 +12,7 @@
     from django.core.validators import EmailValidator
     from django.utils.deprecation import RemovedInDjango60Warning
     from django.utils.functional import Promise, cached_property, keep_lazy, keep_lazy_text
    -from django.utils.http import RFC3986_GENDELIMS, RFC3986_SUBDELIMS
    +from django.utils.http import MAX_URL_LENGTH, RFC3986_GENDELIMS, RFC3986_SUBDELIMS
     from django.utils.regex_helper import _lazy_re_compile
     from django.utils.safestring import SafeData, SafeString, mark_safe
     from django.utils.text import normalize_newlines
    @@ -40,7 +40,6 @@
         )
     )
     
    -MAX_URL_LENGTH = 2048
     MAX_STRIP_TAGS_DEPTH = 50
     
     
    
  • django/utils/http.py+5 1 modified
    @@ -37,6 +37,7 @@
     
     RFC3986_GENDELIMS = ":/?#[]@"
     RFC3986_SUBDELIMS = "!$&'()*+,;="
    +MAX_URL_LENGTH = 2048
     
     
     def urlencode(query, doseq=False):
    @@ -272,7 +273,10 @@ def url_has_allowed_host_and_scheme(url, allowed_hosts, require_https=False):
     def _url_has_allowed_host_and_scheme(url, allowed_hosts, require_https=False):
         # Chrome considers any URL with more than two slashes to be absolute, but
         # urlsplit is not so flexible. Treat any url with three slashes as unsafe.
    -    if url.startswith("///"):
    +    if url.startswith("///") or len(url) > MAX_URL_LENGTH:
    +        # urlsplit does not perform validation of inputs. Unicode normalization
    +        # is very slow on Windows and can be a DoS attack vector.
    +        # https://docs.python.org/3/library/urllib.parse.html#url-parsing-security
             return False
         try:
             url_info = urlsplit(url)
    
  • docs/releases/5.0.14.txt+10 0 modified
    @@ -5,3 +5,13 @@ Django 5.0.14 release notes
     *April 2, 2025*
     
     Django 5.0.14 fixes a security issue with severity "moderate" in 5.0.13.
    +
    +CVE-2025-27556: Potential denial-of-service vulnerability in ``LoginView``, ``LogoutView``, and ``set_language()`` on Windows
    +=============================================================================================================================
    +
    +Python's :func:`NFKC normalization <python:unicodedata.normalize>` is slow on
    +Windows. As a consequence, :class:`~django.contrib.auth.views.LoginView`,
    +:class:`~django.contrib.auth.views.LogoutView`, and
    +:func:`~django.views.i18n.set_language` were subject to a potential
    +denial-of-service attack via certain inputs with a very large number of Unicode
    +characters.
    
  • docs/releases/5.1.8.txt+10 0 modified
    @@ -7,6 +7,16 @@ Django 5.1.8 release notes
     Django 5.1.8 fixes a security issue with severity "moderate" and several bugs
     in 5.1.7.
     
    +CVE-2025-27556: Potential denial-of-service vulnerability in ``LoginView``, ``LogoutView``, and ``set_language()`` on Windows
    +=============================================================================================================================
    +
    +Python's :func:`NFKC normalization <python:unicodedata.normalize>` is slow on
    +Windows. As a consequence, :class:`~django.contrib.auth.views.LoginView`,
    +:class:`~django.contrib.auth.views.LogoutView`, and
    +:func:`~django.views.i18n.set_language` were subject to a potential
    +denial-of-service attack via certain inputs with a very large number of Unicode
    +characters.
    +
     Bugfixes
     ========
     
    
  • tests/utils_tests/test_http.py+16 0 modified
    @@ -6,6 +6,7 @@
     from django.test import SimpleTestCase
     from django.utils.datastructures import MultiValueDict
     from django.utils.http import (
    +    MAX_URL_LENGTH,
         base36_to_int,
         content_disposition_header,
         escape_leading_slashes,
    @@ -273,6 +274,21 @@ def test_secure_param_non_https_urls(self):
                         False,
                     )
     
    +    def test_max_url_length(self):
    +        allowed_host = "example.com"
    +        max_extra_characters = "é" * (MAX_URL_LENGTH - len(allowed_host) - 1)
    +        max_length_boundary_url = f"{allowed_host}/{max_extra_characters}"
    +        cases = [
    +            (max_length_boundary_url, True),
    +            (max_length_boundary_url + "ú", False),
    +        ]
    +        for url, expected in cases:
    +            with self.subTest(url=url):
    +                self.assertIs(
    +                    url_has_allowed_host_and_scheme(url, allowed_hosts={allowed_host}),
    +                    expected,
    +                )
    +
     
     class URLSafeBase64Tests(unittest.TestCase):
         def test_roundtrip(self):
    
8c6871b097b6

[5.0.x] Fixed CVE-2025-27556 -- Mitigated potential DoS in url_has_allowed_host_and_scheme() on Windows.

https://github.com/django/djangoSarah BoyceMar 6, 2025via ghsa
5 files changed · +34 4
  • django/core/validators.py+2 1 modified
    @@ -7,6 +7,7 @@
     from django.core.exceptions import ValidationError
     from django.utils.deconstruct import deconstructible
     from django.utils.encoding import punycode
    +from django.utils.http import MAX_URL_LENGTH
     from django.utils.ipv6 import is_valid_ipv6_address
     from django.utils.regex_helper import _lazy_re_compile
     from django.utils.translation import gettext_lazy as _
    @@ -104,7 +105,7 @@ class URLValidator(RegexValidator):
         message = _("Enter a valid URL.")
         schemes = ["http", "https", "ftp", "ftps"]
         unsafe_chars = frozenset("\t\r\n")
    -    max_length = 2048
    +    max_length = MAX_URL_LENGTH
     
         def __init__(self, schemes=None, **kwargs):
             super().__init__(**kwargs)
    
  • django/utils/html.py+1 2 modified
    @@ -11,7 +11,7 @@
     from django.utils.deprecation import RemovedInDjango60Warning
     from django.utils.encoding import punycode
     from django.utils.functional import Promise, cached_property, keep_lazy, keep_lazy_text
    -from django.utils.http import RFC3986_GENDELIMS, RFC3986_SUBDELIMS
    +from django.utils.http import MAX_URL_LENGTH, RFC3986_GENDELIMS, RFC3986_SUBDELIMS
     from django.utils.regex_helper import _lazy_re_compile
     from django.utils.safestring import SafeData, SafeString, mark_safe
     from django.utils.text import normalize_newlines
    @@ -37,7 +37,6 @@
         "spacer",
     }
     
    -MAX_URL_LENGTH = 2048
     MAX_STRIP_TAGS_DEPTH = 50
     
     
    
  • django/utils/http.py+5 1 modified
    @@ -37,6 +37,7 @@
     
     RFC3986_GENDELIMS = ":/?#[]@"
     RFC3986_SUBDELIMS = "!$&'()*+,;="
    +MAX_URL_LENGTH = 2048
     
     
     def urlencode(query, doseq=False):
    @@ -273,7 +274,10 @@ def url_has_allowed_host_and_scheme(url, allowed_hosts, require_https=False):
     def _url_has_allowed_host_and_scheme(url, allowed_hosts, require_https=False):
         # Chrome considers any URL with more than two slashes to be absolute, but
         # urlparse is not so flexible. Treat any url with three slashes as unsafe.
    -    if url.startswith("///"):
    +    if url.startswith("///") or len(url) > MAX_URL_LENGTH:
    +        # urlparse does not perform validation of inputs. Unicode normalization
    +        # is very slow on Windows and can be a DoS attack vector.
    +        # https://docs.python.org/3/library/urllib.parse.html#url-parsing-security
             return False
         try:
             url_info = urlparse(url)
    
  • docs/releases/5.0.14.txt+10 0 modified
    @@ -5,3 +5,13 @@ Django 5.0.14 release notes
     *April 2, 2025*
     
     Django 5.0.14 fixes a security issue with severity "moderate" in 5.0.13.
    +
    +CVE-2025-27556: Potential denial-of-service vulnerability in ``LoginView``, ``LogoutView``, and ``set_language()`` on Windows
    +=============================================================================================================================
    +
    +Python's :func:`NFKC normalization <python:unicodedata.normalize>` is slow on
    +Windows. As a consequence, :class:`~django.contrib.auth.views.LoginView`,
    +:class:`~django.contrib.auth.views.LogoutView`, and
    +:func:`~django.views.i18n.set_language` were subject to a potential
    +denial-of-service attack via certain inputs with a very large number of Unicode
    +characters.
    
  • tests/utils_tests/test_http.py+16 0 modified
    @@ -6,6 +6,7 @@
     from django.test import SimpleTestCase
     from django.utils.datastructures import MultiValueDict
     from django.utils.http import (
    +    MAX_URL_LENGTH,
         base36_to_int,
         content_disposition_header,
         escape_leading_slashes,
    @@ -273,6 +274,21 @@ def test_secure_param_non_https_urls(self):
                         False,
                     )
     
    +    def test_max_url_length(self):
    +        allowed_host = "example.com"
    +        max_extra_characters = "é" * (MAX_URL_LENGTH - len(allowed_host) - 1)
    +        max_length_boundary_url = f"{allowed_host}/{max_extra_characters}"
    +        cases = [
    +            (max_length_boundary_url, True),
    +            (max_length_boundary_url + "ú", False),
    +        ]
    +        for url, expected in cases:
    +            with self.subTest(url=url):
    +                self.assertIs(
    +                    url_has_allowed_host_and_scheme(url, allowed_hosts={allowed_host}),
    +                    expected,
    +                )
    +
     
     class URLSafeBase64Tests(unittest.TestCase):
         def test_roundtrip(self):
    
edc2716d01a6

[5.1.x] Fixed CVE-2025-27556 -- Mitigated potential DoS in url_has_allowed_host_and_scheme() on Windows.

https://github.com/django/djangoSarah BoyceMar 6, 2025via ghsa
6 files changed · +44 4
  • django/core/validators.py+2 1 modified
    @@ -7,6 +7,7 @@
     from django.core.exceptions import ValidationError
     from django.utils.deconstruct import deconstructible
     from django.utils.encoding import punycode
    +from django.utils.http import MAX_URL_LENGTH
     from django.utils.ipv6 import is_valid_ipv6_address
     from django.utils.regex_helper import _lazy_re_compile
     from django.utils.translation import gettext_lazy as _
    @@ -155,7 +156,7 @@ class URLValidator(RegexValidator):
         message = _("Enter a valid URL.")
         schemes = ["http", "https", "ftp", "ftps"]
         unsafe_chars = frozenset("\t\r\n")
    -    max_length = 2048
    +    max_length = MAX_URL_LENGTH
     
         def __init__(self, schemes=None, **kwargs):
             super().__init__(**kwargs)
    
  • django/utils/html.py+1 2 modified
    @@ -11,7 +11,7 @@
     from django.utils.deprecation import RemovedInDjango60Warning
     from django.utils.encoding import punycode
     from django.utils.functional import Promise, cached_property, keep_lazy, keep_lazy_text
    -from django.utils.http import RFC3986_GENDELIMS, RFC3986_SUBDELIMS
    +from django.utils.http import MAX_URL_LENGTH, RFC3986_GENDELIMS, RFC3986_SUBDELIMS
     from django.utils.regex_helper import _lazy_re_compile
     from django.utils.safestring import SafeData, SafeString, mark_safe
     from django.utils.text import normalize_newlines
    @@ -39,7 +39,6 @@
         )
     )
     
    -MAX_URL_LENGTH = 2048
     MAX_STRIP_TAGS_DEPTH = 50
     
     
    
  • django/utils/http.py+5 1 modified
    @@ -37,6 +37,7 @@
     
     RFC3986_GENDELIMS = ":/?#[]@"
     RFC3986_SUBDELIMS = "!$&'()*+,;="
    +MAX_URL_LENGTH = 2048
     
     
     def urlencode(query, doseq=False):
    @@ -272,7 +273,10 @@ def url_has_allowed_host_and_scheme(url, allowed_hosts, require_https=False):
     def _url_has_allowed_host_and_scheme(url, allowed_hosts, require_https=False):
         # Chrome considers any URL with more than two slashes to be absolute, but
         # urlparse is not so flexible. Treat any url with three slashes as unsafe.
    -    if url.startswith("///"):
    +    if url.startswith("///") or len(url) > MAX_URL_LENGTH:
    +        # urlparse does not perform validation of inputs. Unicode normalization
    +        # is very slow on Windows and can be a DoS attack vector.
    +        # https://docs.python.org/3/library/urllib.parse.html#url-parsing-security
             return False
         try:
             url_info = urlparse(url)
    
  • docs/releases/5.0.14.txt+10 0 modified
    @@ -5,3 +5,13 @@ Django 5.0.14 release notes
     *April 2, 2025*
     
     Django 5.0.14 fixes a security issue with severity "moderate" in 5.0.13.
    +
    +CVE-2025-27556: Potential denial-of-service vulnerability in ``LoginView``, ``LogoutView``, and ``set_language()`` on Windows
    +=============================================================================================================================
    +
    +Python's :func:`NFKC normalization <python:unicodedata.normalize>` is slow on
    +Windows. As a consequence, :class:`~django.contrib.auth.views.LoginView`,
    +:class:`~django.contrib.auth.views.LogoutView`, and
    +:func:`~django.views.i18n.set_language` were subject to a potential
    +denial-of-service attack via certain inputs with a very large number of Unicode
    +characters.
    
  • docs/releases/5.1.8.txt+10 0 modified
    @@ -7,6 +7,16 @@ Django 5.1.8 release notes
     Django 5.1.8 fixes a security issue with severity "moderate" and several bugs
     in 5.1.7.
     
    +CVE-2025-27556: Potential denial-of-service vulnerability in ``LoginView``, ``LogoutView``, and ``set_language()`` on Windows
    +=============================================================================================================================
    +
    +Python's :func:`NFKC normalization <python:unicodedata.normalize>` is slow on
    +Windows. As a consequence, :class:`~django.contrib.auth.views.LoginView`,
    +:class:`~django.contrib.auth.views.LogoutView`, and
    +:func:`~django.views.i18n.set_language` were subject to a potential
    +denial-of-service attack via certain inputs with a very large number of Unicode
    +characters.
    +
     Bugfixes
     ========
     
    
  • tests/utils_tests/test_http.py+16 0 modified
    @@ -6,6 +6,7 @@
     from django.test import SimpleTestCase
     from django.utils.datastructures import MultiValueDict
     from django.utils.http import (
    +    MAX_URL_LENGTH,
         base36_to_int,
         content_disposition_header,
         escape_leading_slashes,
    @@ -273,6 +274,21 @@ def test_secure_param_non_https_urls(self):
                         False,
                     )
     
    +    def test_max_url_length(self):
    +        allowed_host = "example.com"
    +        max_extra_characters = "é" * (MAX_URL_LENGTH - len(allowed_host) - 1)
    +        max_length_boundary_url = f"{allowed_host}/{max_extra_characters}"
    +        cases = [
    +            (max_length_boundary_url, True),
    +            (max_length_boundary_url + "ú", False),
    +        ]
    +        for url, expected in cases:
    +            with self.subTest(url=url):
    +                self.assertIs(
    +                    url_has_allowed_host_and_scheme(url, allowed_hosts={allowed_host}),
    +                    expected,
    +                )
    +
     
     class URLSafeBase64Tests(unittest.TestCase):
         def test_roundtrip(self):
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

13

News mentions

0

No linked articles in our index yet.