A RetroSearch Logo

Home - News ( United States | United Kingdom | Italy | Germany ) - Football scores

Search Query:

Showing content from https://github.com/django/django/commit/f4cff43bf921fcea6a29b726eb66767f67753fa2 below:

[1.11.x] Fixed CVE-2019-19844 -- Used verified user email for passwor… · django/django@f4cff43 · GitHub

File tree Expand file treeCollapse file tree 3 files changed

+86

-6

lines changed

Filter options

Expand file treeCollapse file tree 3 files changed

+86

-6

lines changed Original file line number Diff line number Diff line change

@@ -16,12 +16,27 @@

16 16

from django.template import loader

17 17

from django.utils.encoding import force_bytes

18 18

from django.utils.http import urlsafe_base64_encode

19 +

from django.utils.six import PY3

19 20

from django.utils.text import capfirst

20 21

from django.utils.translation import ugettext, ugettext_lazy as _

21 22 22 23

UserModel = get_user_model()

23 24 24 25 26 +

def _unicode_ci_compare(s1, s2):

27 +

"""

28 +

Perform case-insensitive comparison of two identifiers, using the

29 +

recommended algorithm from Unicode Technical Report 36, section

30 +

2.11.2(B)(2).

31 +

"""

32 +

normalized1 = unicodedata.normalize('NFKC', s1)

33 +

normalized2 = unicodedata.normalize('NFKC', s2)

34 +

if PY3:

35 +

return normalized1.casefold() == normalized2.casefold()

36 +

# lower() is the best alternative available on Python 2.

37 +

return normalized1.lower() == normalized2.lower()

38 + 39 + 25 40

class ReadOnlyPasswordHashWidget(forms.Widget):

26 41

template_name = 'auth/widgets/read_only_password_hash.html'

27 42

@@ -249,11 +264,16 @@ def get_users(self, email):

249 264

that prevent inactive users and users with unusable passwords from

250 265

resetting their password.

251 266

"""

267 +

email_field_name = UserModel.get_email_field_name()

252 268

active_users = UserModel._default_manager.filter(**{

253 -

'%s__iexact' % UserModel.get_email_field_name(): email,

269 +

'%s__iexact' % email_field_name: email,

254 270

'is_active': True,

255 271

})

256 -

return (u for u in active_users if u.has_usable_password())

272 +

return (

273 +

u for u in active_users

274 +

if u.has_usable_password() and

275 +

_unicode_ci_compare(email, getattr(u, email_field_name))

276 +

)

257 277 258 278

def save(self, domain_override=None,

259 279

subject_template_name='registration/password_reset_subject.txt',

@@ -266,15 +286,17 @@ def save(self, domain_override=None,

266 286

user.

267 287

"""

268 288

email = self.cleaned_data["email"]

289 +

email_field_name = UserModel.get_email_field_name()

269 290

for user in self.get_users(email):

270 291

if not domain_override:

271 292

current_site = get_current_site(request)

272 293

site_name = current_site.name

273 294

domain = current_site.domain

274 295

else:

275 296

site_name = domain = domain_override

297 +

user_email = getattr(user, email_field_name)

276 298

context = {

277 -

'email': email,

299 +

'email': user_email,

278 300

'domain': domain,

279 301

'site_name': site_name,

280 302

'uid': urlsafe_base64_encode(force_bytes(user.pk)),

@@ -286,7 +308,7 @@ def save(self, domain_override=None,

286 308

context.update(extra_email_context)

287 309

self.send_mail(

288 310

subject_template_name, email_template_name, context, from_email,

289 -

email, html_email_template_name=html_email_template_name,

311 +

user_email, html_email_template_name=html_email_template_name,

290 312

)

291 313 292 314 Original file line number Diff line number Diff line change

@@ -2,9 +2,25 @@

2 2

Django 1.11.27 release notes

3 3

============================

4 4 5 -

*Expected January 2, 2020*

5 +

*December 18, 2019*

6 6 7 -

Django 1.11.27 fixes a data loss bug in 1.11.26.

7 +

Django 1.11.27 fixes a security issue and a data loss bug in 1.11.26.

8 + 9 +

CVE-2019-19844: Potential account hijack via password reset form

10 +

================================================================

11 + 12 +

By submitting a suitably crafted email address making use of Unicode

13 +

characters, that compared equal to an existing user email when lower-cased for

14 +

comparison, an attacker could be sent a password reset token for the matched

15 +

account.

16 + 17 +

In order to avoid this vulnerability, password reset requests now compare the

18 +

submitted email using the stricter, recommended algorithm for case-insensitive

19 +

comparison of two identifiers from `Unicode Technical Report 36, section

20 +

2.11.2(B)(2)`__. Upon a match, the email containing the reset token will be

21 +

sent to the email address on record rather than the submitted address.

22 + 23 +

.. __: https://www.unicode.org/reports/tr36/#Recommendations_General

8 24 9 25

Bugfixes

10 26

========

Original file line number Diff line number Diff line change

@@ -694,6 +694,48 @@ def test_invalid_email(self):

694 694

self.assertFalse(form.is_valid())

695 695

self.assertEqual(form['email'].errors, [_('Enter a valid email address.')])

696 696 697 +

def test_user_email_unicode_collision(self):

698 +

User.objects.create_user('mike123', 'mike@example.org', 'test123')

699 +

User.objects.create_user('mike456', 'mıke@example.org', 'test123')

700 +

data = {'email': 'mıke@example.org'}

701 +

form = PasswordResetForm(data)

702 +

if six.PY2:

703 +

self.assertFalse(form.is_valid())

704 +

else:

705 +

self.assertTrue(form.is_valid())

706 +

form.save()

707 +

self.assertEqual(len(mail.outbox), 1)

708 +

self.assertEqual(mail.outbox[0].to, ['mıke@example.org'])

709 + 710 +

def test_user_email_domain_unicode_collision(self):

711 +

User.objects.create_user('mike123', 'mike@ixample.org', 'test123')

712 +

User.objects.create_user('mike456', 'mike@ıxample.org', 'test123')

713 +

data = {'email': 'mike@ıxample.org'}

714 +

form = PasswordResetForm(data)

715 +

self.assertTrue(form.is_valid())

716 +

form.save()

717 +

self.assertEqual(len(mail.outbox), 1)

718 +

self.assertEqual(mail.outbox[0].to, ['mike@ıxample.org'])

719 + 720 +

def test_user_email_unicode_collision_nonexistent(self):

721 +

User.objects.create_user('mike123', 'mike@example.org', 'test123')

722 +

data = {'email': 'mıke@example.org'}

723 +

form = PasswordResetForm(data)

724 +

if six.PY2:

725 +

self.assertFalse(form.is_valid())

726 +

else:

727 +

self.assertTrue(form.is_valid())

728 +

form.save()

729 +

self.assertEqual(len(mail.outbox), 0)

730 + 731 +

def test_user_email_domain_unicode_collision_nonexistent(self):

732 +

User.objects.create_user('mike123', 'mike@ixample.org', 'test123')

733 +

data = {'email': 'mike@ıxample.org'}

734 +

form = PasswordResetForm(data)

735 +

self.assertTrue(form.is_valid())

736 +

form.save()

737 +

self.assertEqual(len(mail.outbox), 0)

738 + 697 739

def test_nonexistent_email(self):

698 740

"""

699 741

Test nonexistent email address. This should not fail because it would

You can’t perform that action at this time.


RetroSearch is an open source project built by @garambo | Open a GitHub Issue

Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo

HTML: 3.2 | Encoding: UTF-8 | Version: 0.7.4