#31139: PasswordResetView return success message for emails not in database also
-------------------------------------+-------------------------------------
Reporter: SANYAM MITTAL | Owner:
Type: | sanyam19092000
Cleanup/optimization | Status: new
Component: contrib.auth | Version: master
Severity: Normal | Resolution:
Keywords: | Triage Stage:
| Unreviewed
Has patch: 0 | Needs documentation: 1
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 1 | UI/UX: 0
-------------------------------------+-------------------------------------
Changes (by SANYAM MITTAL):
* status: closed => new
* needs_docs: 0 => 1
* resolution: wontfix =>
Comment:
Thanks for the Reference,
As mentioned in documentation
[https://docs.djangoproject.com/en/stable/topics/auth/default/#django.contrib.auth.views.PasswordResetView]
''** This prevents information leaking to potential attackers**''
Facebook also raises a Validation Error when non registered email is
entered
[https://www.facebook.com/login/identify/?ctx=recover]
----
Although a potential attacker can easily get these information from Sign-
Up/Register page as **Validation error is raised when a Duplicate Email
Address** is entered during sign-up.
If there's **not a Unique email Validation** during Sign-up there are
chances that multiple users get registered with same email (if user
mistakenly types someone else's email) and Password Reset email is sent
multiple times for different Users which is more risky.
----
**If still it prevents Information leak by any other ways** then
documentation should be more accurate and should have a sample or
**Suggestion method** to achieve unique email Validation with current
PasswordResetForm like
If you want to provide an error message in this case, you can subclass
**PasswordResetForm** and use the **form_class attribute**.
{{{
class PasswordResetForm(forms.Form):
error_messages = {
'not_registered': _('Email ID is not Registered'),
}
email = forms.EmailField(
label=_("Email"),
max_length=254,
widget=forms.EmailInput(attrs={'autocomplete': 'email'})
)
def clean_email(self):
email = self.cleaned_data.get('email')
users = self.get_users(email=email)
flag = False
for user in users:
if user:
flag = True
if not flag:
raise forms.ValidationError(
self.error_messages['not_registered'],
code='not_registered',
)
return email
def send_mail(self, subject_template_name, email_template_name,
context, from_email, to_email,
html_email_template_name=None):
"""
Send a django.core.mail.EmailMultiAlternatives to `to_email`.
"""
subject = loader.render_to_string(subject_template_name, context)
# Email subject *must not* contain newlines
subject = 'Testing Purpose'
body = loader.render_to_string(email_template_name, context)
email_message = EmailMultiAlternatives(subject, body, from_email,
[to_email])
if html_email_template_name is not None:
html_email = loader.render_to_string(html_email_template_name,
context)
email_message.attach_alternative(html_email, 'text/html')
email_message.send()
def get_users(self, email):
"""Given an email, return matching user(s) who should receive a
reset.
This allows subclasses to more easily customize the default
policies
that prevent inactive users and users with unusable passwords from
resetting their password.
"""
email_field_name = UserModel.get_email_field_name()
active_users = UserModel._default_manager.filter(**{
'%s__iexact' % email_field_name: email,
'is_active': True,
})
return (
u for u in active_users
if u.has_usable_password() and
_unicode_ci_compare(email, getattr(u, email_field_name))
)
def save(self, domain_override=None,
subject_template_name='registration/password_reset_subject.txt',
email_template_name='registration/password_reset_email.html',
use_https=False, token_generator=default_token_generator,
from_email=None, request=None, html_email_template_name=None,
extra_email_context=None):
"""
Generate a one-use only link for resetting password and send it to
the
user.
"""
email = self.cleaned_data["email"]
email_field_name = UserModel.get_email_field_name()
for user in self.get_users(email):
if not domain_override:
current_site = get_current_site(request)
site_name = current_site.name
domain = current_site.domain
else:
site_name = domain = domain_override
user_email = getattr(user, email_field_name)
context = {
'email': user_email,
'domain': domain,
'site_name': site_name,
'uid': urlsafe_base64_encode(force_bytes(user.pk)),
'user': user,
'token': token_generator.make_token(user),
'protocol': 'https' if use_https else 'http',
**(extra_email_context or {}),
}
self.send_mail(
subject_template_name, email_template_name, context,
from_email,
user_email,
html_email_template_name=html_email_template_name,
)
}}}
--
Ticket URL: <https://code.djangoproject.com/ticket/31139#comment:2>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.
--
You received this message because you are subscribed to the Google Groups
"Django updates" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To view this discussion on the web visit
https://groups.google.com/d/msgid/django-updates/071.b0b774f9abfe74bd56c8e89aae2004a9%40djangoproject.com.