Skip to content

Commit

Permalink
[3.2.x] Fixed CVE-2023-46695 -- Fixed potential DoS in UsernameField …
Browse files Browse the repository at this point in the history
…on Windows.

Thanks MProgrammer (https://hackerone.com/mprogrammer) for the report.
  • Loading branch information
felixxm committed Nov 1, 2023
1 parent e6d2591 commit f9a7fb8
Show file tree
Hide file tree
Showing 3 changed files with 27 additions and 3 deletions.
10 changes: 9 additions & 1 deletion django/contrib/auth/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,15 @@ def __init__(self, *args, **kwargs):

class UsernameField(forms.CharField):
def to_python(self, value):
return unicodedata.normalize('NFKC', super().to_python(value))
value = super().to_python(value)
if self.max_length is not None and len(value) > self.max_length:
# Normalization can increase the string length (e.g.
# "ff" -> "ff", "½" -> "1⁄2") but cannot reduce it, so there is no
# point in normalizing invalid data. Moreover, Unicode
# normalization is very slow on Windows and can be a DoS attack
# vector.
return value
return unicodedata.normalize("NFKC", value)

def widget_attrs(self, widget):
return {
Expand Down
12 changes: 11 additions & 1 deletion docs/releases/3.2.23.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,14 @@ Django 3.2.23 release notes

Django 3.2.23 fixes a security issue with severity "moderate" in 3.2.22.

...
CVE-2023-46695: Potential denial of service vulnerability in ``UsernameField`` on Windows
=========================================================================================

The :func:`NFKC normalization <python:unicodedata.normalize>` is slow on
Windows. As a consequence, ``django.contrib.auth.forms.UsernameField`` was
subject to a potential denial of service attack via certain inputs with a very
large number of Unicode characters.

In order to avoid the vulnerability, invalid values longer than
``UsernameField.max_length`` are no longer normalized, since they cannot pass
validation anyway.
8 changes: 7 additions & 1 deletion tests/auth_tests/test_forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from django.contrib.auth.forms import (
AdminPasswordChangeForm, AuthenticationForm, PasswordChangeForm,
PasswordResetForm, ReadOnlyPasswordHashField, ReadOnlyPasswordHashWidget,
SetPasswordForm, UserChangeForm, UserCreationForm,
SetPasswordForm, UserChangeForm, UserCreationForm, UsernameField,
)
from django.contrib.auth.models import User
from django.contrib.auth.signals import user_login_failed
Expand Down Expand Up @@ -132,6 +132,12 @@ def test_normalize_username(self):
self.assertNotEqual(user.username, ohm_username)
self.assertEqual(user.username, 'testΩ') # U+03A9 GREEK CAPITAL LETTER OMEGA

def test_invalid_username_no_normalize(self):
field = UsernameField(max_length=254)
# Usernames are not normalized if they are too long.
self.assertEqual(field.to_python("½" * 255), "½" * 255)
self.assertEqual(field.to_python("ff" * 254), "ff" * 254)

def test_duplicate_normalized_unicode(self):
"""
To prevent almost identical usernames, visually identical but differing
Expand Down

0 comments on commit f9a7fb8

Please sign in to comment.