+60
-2
lines changedFilter options
+60
-2
lines changed Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
1
+
import logging
1
2
import unicodedata
2
3
3
4
from django import forms
@@ -16,6 +17,7 @@
16
17
from django.utils.translation import gettext_lazy as _
17
18
18
19
UserModel = get_user_model()
20
+
logger = logging.getLogger("django.contrib.auth")
19
21
20
22
21
23
def _unicode_ci_compare(s1, s2):
@@ -314,7 +316,12 @@ def send_mail(
314
316
html_email = loader.render_to_string(html_email_template_name, context)
315
317
email_message.attach_alternative(html_email, "text/html")
316
318
317
-
email_message.send()
319
+
try:
320
+
email_message.send()
321
+
except Exception:
322
+
logger.exception(
323
+
"Failed to send password reset email to %s", context["user"].pk
324
+
)
318
325
319
326
def get_users(self, email):
320
327
"""Given an email, return matching user(s) who should receive a reset.
Original file line number Diff line number Diff line change
@@ -204,6 +204,18 @@ all database queries.
204
204
Support for logging transaction management queries (``BEGIN``, ``COMMIT``,
205
205
and ``ROLLBACK``) was added.
206
206
207
+
.. _django-contrib-auth-logger:
208
+
209
+
``django.contrib.auth``
210
+
~~~~~~~~~~~~~~~~~~~~~~~
211
+
212
+
.. versionadded:: 4.2.16
213
+
214
+
Log messages related to :doc:`contrib/auth`, particularly ``ERROR`` messages
215
+
are generated when a :class:`~django.contrib.auth.forms.PasswordResetForm` is
216
+
successfully submitted but the password reset email cannot be delivered due to
217
+
a mail sending exception.
218
+
207
219
.. _django-security-logger:
208
220
209
221
``django.security.*``
Original file line number Diff line number Diff line change
@@ -13,3 +13,14 @@ CVE-2024-45230: Potential denial-of-service vulnerability in ``django.utils.html
13
13
:tfilter:`urlize` and :tfilter:`urlizetrunc` were subject to a potential
14
14
denial-of-service attack via very large inputs with a specific sequence of
15
15
characters.
16
+
17
+
CVE-2024-45231: Potential user email enumeration via response status on password reset
18
+
======================================================================================
19
+
20
+
Due to unhandled email sending failures, the
21
+
:class:`~django.contrib.auth.forms.PasswordResetForm` class allowed remote
22
+
attackers to enumerate user emails by issuing password reset requests and
23
+
observing the outcomes.
24
+
25
+
To mitigate this risk, exceptions occurring during password reset email sending
26
+
are now handled and logged using the :ref:`django-contrib-auth-logger` logger.
Original file line number Diff line number Diff line change
@@ -1661,7 +1661,9 @@ provides several built-in forms located in :mod:`django.contrib.auth.forms`:
1661
1661
.. method:: send_mail(subject_template_name, email_template_name, context, from_email, to_email, html_email_template_name=None)
1662
1662
1663
1663
Uses the arguments to send an ``EmailMultiAlternatives``.
1664
-
Can be overridden to customize how the email is sent to the user.
1664
+
Can be overridden to customize how the email is sent to the user. If
1665
+
you choose to override this method, be mindful of handling potential
1666
+
exceptions raised due to email sending failures.
1665
1667
1666
1668
:param subject_template_name: the template for the subject.
1667
1669
:param email_template_name: the template for the email body.
Original file line number Diff line number Diff line change
@@ -1245,6 +1245,27 @@ def test_save_html_email_template_name(self):
1245
1245
)
1246
1246
)
1247
1247
1248
+
@override_settings(EMAIL_BACKEND="mail.custombackend.FailingEmailBackend")
1249
+
def test_save_send_email_exceptions_are_catched_and_logged(self):
1250
+
(user, username, email) = self.create_dummy_user()
1251
+
form = PasswordResetForm({"email": email})
1252
+
self.assertTrue(form.is_valid())
1253
+
1254
+
with self.assertLogs("django.contrib.auth", level=0) as cm:
1255
+
form.save()
1256
+
1257
+
self.assertEqual(len(mail.outbox), 0)
1258
+
self.assertEqual(len(cm.output), 1)
1259
+
errors = cm.output[0].split("\n")
1260
+
pk = user.pk
1261
+
self.assertEqual(
1262
+
errors[0],
1263
+
f"ERROR:django.contrib.auth:Failed to send password reset email to {pk}",
1264
+
)
1265
+
self.assertEqual(
1266
+
errors[-1], "ValueError: FailingEmailBackend is doomed to fail."
1267
+
)
1268
+
1248
1269
@override_settings(AUTH_USER_MODEL="auth_tests.CustomEmailField")
1249
1270
def test_custom_email_field(self):
1250
1271
email = "test@mail.com"
Original file line number Diff line number Diff line change
@@ -12,3 +12,8 @@ def send_messages(self, email_messages):
12
12
# Messages are stored in an instance variable for testing.
13
13
self.test_outbox.extend(email_messages)
14
14
return len(email_messages)
15
+
16
+
17
+
class FailingEmailBackend(BaseEmailBackend):
18
+
def send_messages(self, email_messages):
19
+
raise ValueError("FailingEmailBackend is doomed to fail.")
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