+110
-10
lines changedFilter options
+110
-10
lines changed Original file line number Diff line number Diff line change
@@ -20,6 +20,15 @@
20
20
UserModel = get_user_model()
21
21
22
22
23
+
def _unicode_ci_compare(s1, s2):
24
+
"""
25
+
Perform case-insensitive comparison of two identifiers, using the
26
+
recommended algorithm from Unicode Technical Report 36, section
27
+
2.11.2(B)(2).
28
+
"""
29
+
return unicodedata.normalize('NFKC', s1).casefold() == unicodedata.normalize('NFKC', s2).casefold()
30
+
31
+
23
32
class ReadOnlyPasswordHashWidget(forms.Widget):
24
33
template_name = 'auth/widgets/read_only_password_hash.html'
25
34
read_only = True
@@ -269,11 +278,16 @@ def get_users(self, email):
269
278
that prevent inactive users and users with unusable passwords from
270
279
resetting their password.
271
280
"""
281
+
email_field_name = UserModel.get_email_field_name()
272
282
active_users = UserModel._default_manager.filter(**{
273
-
'%s__iexact' % UserModel.get_email_field_name(): email,
283
+
'%s__iexact' % email_field_name: email,
274
284
'is_active': True,
275
285
})
276
-
return (u for u in active_users if u.has_usable_password())
286
+
return (
287
+
u for u in active_users
288
+
if u.has_usable_password() and
289
+
_unicode_ci_compare(email, getattr(u, email_field_name))
290
+
)
277
291
278
292
def save(self, domain_override=None,
279
293
subject_template_name='registration/password_reset_subject.txt',
@@ -286,15 +300,17 @@ def save(self, domain_override=None,
286
300
user.
287
301
"""
288
302
email = self.cleaned_data["email"]
303
+
email_field_name = UserModel.get_email_field_name()
289
304
for user in self.get_users(email):
290
305
if not domain_override:
291
306
current_site = get_current_site(request)
292
307
site_name = current_site.name
293
308
domain = current_site.domain
294
309
else:
295
310
site_name = domain = domain_override
311
+
user_email = getattr(user, email_field_name)
296
312
context = {
297
-
'email': email,
313
+
'email': user_email,
298
314
'domain': domain,
299
315
'site_name': site_name,
300
316
'uid': urlsafe_base64_encode(force_bytes(user.pk)),
@@ -305,7 +321,7 @@ def save(self, domain_override=None,
305
321
}
306
322
self.send_mail(
307
323
subject_template_name, email_template_name, context, from_email,
308
-
email, html_email_template_name=html_email_template_name,
324
+
user_email, html_email_template_name=html_email_template_name,
309
325
)
310
326
311
327
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
@@ -2,9 +2,25 @@
2
2
Django 2.2.9 release notes
3
3
==========================
4
4
5
-
*Expected January 2, 2020*
5
+
*December 18, 2019*
6
6
7
-
Django 2.2.9 fixes a data loss bug in 2.2.8.
7
+
Django 2.2.9 fixes a security issue and a data loss bug in 2.2.8.
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
@@ -2,9 +2,25 @@
2
2
Django 3.0.1 release notes
3
3
==========================
4
4
5
-
*Expected January 2, 2020*
5
+
*December 18, 2019*
6
6
7
-
Django 3.0.1 fixes several bugs in 3.0.
7
+
Django 3.0.1 fixes a security issue and several bugs in 3.0.
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
@@ -804,6 +804,42 @@ def test_invalid_email(self):
804
804
self.assertFalse(form.is_valid())
805
805
self.assertEqual(form['email'].errors, [_('Enter a valid email address.')])
806
806
807
+
def test_user_email_unicode_collision(self):
808
+
User.objects.create_user('mike123', 'mike@example.org', 'test123')
809
+
User.objects.create_user('mike456', 'mıke@example.org', 'test123')
810
+
data = {'email': 'mıke@example.org'}
811
+
form = PasswordResetForm(data)
812
+
self.assertTrue(form.is_valid())
813
+
form.save()
814
+
self.assertEqual(len(mail.outbox), 1)
815
+
self.assertEqual(mail.outbox[0].to, ['mıke@example.org'])
816
+
817
+
def test_user_email_domain_unicode_collision(self):
818
+
User.objects.create_user('mike123', 'mike@ixample.org', 'test123')
819
+
User.objects.create_user('mike456', 'mike@ıxample.org', 'test123')
820
+
data = {'email': 'mike@ıxample.org'}
821
+
form = PasswordResetForm(data)
822
+
self.assertTrue(form.is_valid())
823
+
form.save()
824
+
self.assertEqual(len(mail.outbox), 1)
825
+
self.assertEqual(mail.outbox[0].to, ['mike@ıxample.org'])
826
+
827
+
def test_user_email_unicode_collision_nonexistent(self):
828
+
User.objects.create_user('mike123', 'mike@example.org', 'test123')
829
+
data = {'email': 'mıke@example.org'}
830
+
form = PasswordResetForm(data)
831
+
self.assertTrue(form.is_valid())
832
+
form.save()
833
+
self.assertEqual(len(mail.outbox), 0)
834
+
835
+
def test_user_email_domain_unicode_collision_nonexistent(self):
836
+
User.objects.create_user('mike123', 'mike@ixample.org', 'test123')
837
+
data = {'email': 'mike@ıxample.org'}
838
+
form = PasswordResetForm(data)
839
+
self.assertTrue(form.is_valid())
840
+
form.save()
841
+
self.assertEqual(len(mail.outbox), 0)
842
+
807
843
def test_nonexistent_email(self):
808
844
"""
809
845
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