mirror of
https://github.com/zulip/zulip.git
synced 2025-11-04 14:03:30 +00:00
auth: Use zxcvbn to ensure password strength on server side.
For a long time, we've been only doing the zxcvbn password strength checks on the browser, which is helpful, but means users could through hackery (or a bug in the frontend validation code) manage to set a too-weak password. We fix this by running our password strength validation on the backend as well, using python-zxcvbn. In theory, a bug in python-zxcvbn could result in it producing a different opinion than the frontend version; if so, it'd be a pretty bad bug in the library, and hopefully we'd hear about it from users, report upstream, and get it fixed that way. Alternatively, we can switch to shelling out to node like we do for KaTeX. Fixes #6880.
This commit is contained in:
committed by
Tim Abbott
parent
0c2cc41d2e
commit
06c2161f7e
@@ -25,7 +25,7 @@ from zerver.models import Realm, get_user_by_delivery_email, UserProfile, get_re
|
||||
email_to_domain, \
|
||||
email_allowed_for_realm, DisposableEmailError, DomainNotAllowedForRealmError, \
|
||||
EmailContainsPlusError
|
||||
from zproject.backends import email_auth_enabled, email_belongs_to_ldap
|
||||
from zproject.backends import email_auth_enabled, email_belongs_to_ldap, check_password_strength
|
||||
|
||||
import logging
|
||||
import re
|
||||
@@ -44,6 +44,7 @@ WRONG_SUBDOMAIN_ERROR = "Your Zulip account is not a member of the " + \
|
||||
"Please contact your organization administrator with any questions."
|
||||
DEACTIVATED_ACCOUNT_ERROR = u"Your account is no longer active. " + \
|
||||
u"Please contact your organization administrator to reactivate it."
|
||||
PASSWORD_TOO_WEAK_ERROR = u"The password is too weak."
|
||||
|
||||
def email_is_not_mit_mailing_list(email: str) -> None:
|
||||
"""Prevent MIT mailing lists from signing up for Zulip"""
|
||||
@@ -108,6 +109,15 @@ class RegistrationForm(forms.Form):
|
||||
except JsonableError as e:
|
||||
raise ValidationError(e.msg)
|
||||
|
||||
def clean_password(self) -> str:
|
||||
password = self.cleaned_data['password']
|
||||
if self.fields['password'].required and not check_password_strength(password):
|
||||
# The frontend code tries to stop the user from submitting the form with a weak password,
|
||||
# but if the user bypasses that protection, this error code path will run.
|
||||
raise ValidationError(mark_safe(PASSWORD_TOO_WEAK_ERROR))
|
||||
|
||||
return password
|
||||
|
||||
def clean_realm_subdomain(self) -> str:
|
||||
if not self.realm_creation:
|
||||
# This field is only used if realm_creation
|
||||
@@ -179,6 +189,15 @@ class RealmCreationForm(forms.Form):
|
||||
email_is_not_disposable])
|
||||
|
||||
class LoggingSetPasswordForm(SetPasswordForm):
|
||||
def clean_new_password1(self) -> str:
|
||||
new_password = self.cleaned_data['new_password1']
|
||||
if not check_password_strength(new_password):
|
||||
# The frontend code tries to stop the user from submitting the form with a weak password,
|
||||
# but if the user bypasses that protection, this error code path will run.
|
||||
raise ValidationError(PASSWORD_TOO_WEAK_ERROR)
|
||||
|
||||
return new_password
|
||||
|
||||
def save(self, commit: bool=True) -> UserProfile:
|
||||
do_change_password(self.user, self.cleaned_data['new_password1'],
|
||||
commit=commit)
|
||||
|
||||
Reference in New Issue
Block a user