Enforcing password strength requirements with django.contrib.auth.views.password_change
I also went with a custom form for this. In urls.py
specify your custom form:
(r'^change_password/$', 'django.contrib.auth.views.password_change', {'password_change_form': ValidatingPasswordChangeForm}),
Inherit from PasswordChangeForm
and implement validation:
from django import formsfrom django.contrib import authclass ValidatingPasswordChangeForm(auth.forms.PasswordChangeForm): MIN_LENGTH = 8 def clean_new_password1(self): password1 = self.cleaned_data.get('new_password1') # At least MIN_LENGTH long if len(password1) < self.MIN_LENGTH: raise forms.ValidationError("The new password must be at least %d characters long." % self.MIN_LENGTH) # At least one letter and one non-letter first_isalpha = password1[0].isalpha() if all(c.isalpha() == first_isalpha for c in password1): raise forms.ValidationError("The new password must contain at least one letter and at least one digit or" \ " punctuation character.") # ... any other validation you want ... return password1
Django 1.9 offers a built-in password validation to help prevent the usage of weak passwords by users. It's enabled by modifing the AUTH_PASSWORD_VALIDATORS
setting in our project. By default Django comes with following validators:
UserAttributeSimilarityValidator
, which checks the similarity betweenthe password and a set of attributes of the user.MinimumLengthValidator
, which simply checks whether the passwordmeets a minimum length. This validator is configured with a customoption: it now requires the minimum length to be nine characters,instead of the default eight.CommonPasswordValidator
, which checkswhether the password occurs in a list of common passwords. Bydefault, it compares to an included list of 1000 common passwords.NumericPasswordValidator
, which checks whether the password isn’tentirely numeric.
This example enables all four included validators:
AUTH_PASSWORD_VALIDATORS = [ { 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', }, { 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', 'OPTIONS': { 'min_length': 9, } }, { 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', }, { 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', },]
As some eluded to with the custom validators, here's the approach I would take...
Create a validator:
from django.core.exceptions import ValidationErrorfrom django.utils.translation import ugettext as _def validate_password_strength(value): """Validates that a password is as least 7 characters long and has at least 1 digit and 1 letter. """ min_length = 7 if len(value) < min_length: raise ValidationError(_('Password must be at least {0} characters ' 'long.').format(min_length)) # check for digit if not any(char.isdigit() for char in value): raise ValidationError(_('Password must contain at least 1 digit.')) # check for letter if not any(char.isalpha() for char in value): raise ValidationError(_('Password must contain at least 1 letter.'))
Then add the validator to the form field you're looking to validate:
from django.contrib.auth.forms import SetPasswordFormclass MySetPasswordForm(SetPasswordForm): def __init__(self, *args, **kwargs): super(MySetPasswordForm, self).__init__(*args, **kwargs) self.fields['new_password1'].validators.append(validate_password_strength)