Have you been "volunteered" to eliminate weak passwords? You can customize Django to enforce stronger password requirements. And Django makes it easy to add your own password validation into the mix when it is creating new users or allowing users to set a new password.
Django includes some basic validators which can come in handy, such as enforcing a minimum length. There is even one to block any password appearing on a list of the 1,000 most common passwords.
To enable password validation, simply set your Django settings as follows:
AUTH_PASSWORD_VALIDATORS = [ { 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', 'OPTIONS': { 'min_length': 9, } }, { 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', }, ]
If you have more specific needs, you can create your own validators. To do so, simply create your own classes based on object and raise a ValidationError
if the entered password fails.
In my app, I created validators.py, which contained four custom validators (1 digit character, 1 uppercase character, 1 lowercase and 1 symbol):
import re from django.core.exceptions import ValidationError from django.utils.translation import ugettext as _
class NumberValidator(object): def validate(self, password, user=None): if not re.findall('\d', password): raise ValidationError( _("The password must contain at least 1 digit, 0-9."), code='password_no_number', ) def get_help_text(self): return _( "Your password must contain at least 1 digit, 0-9." ) class UppercaseValidator(object): def validate(self, password, user=None): if not re.findall('[A-Z]', password): raise ValidationError( _("The password must contain at least 1 uppercase letter, A-Z."), code='password_no_upper', ) def get_help_text(self): return _( "Your password must contain at least 1 uppercase letter, A-Z." ) class LowercaseValidator(object): def validate(self, password, user=None): if not re.findall('[a-z]', password): raise ValidationError( _("The password must contain at least 1 lowercase letter, a-z."), code='password_no_lower', ) def get_help_text(self): return _( "Your password must contain at least 1 lowercase letter, a-z." ) class SymbolValidator(object): def validate(self, password, user=None): if not re.findall('[()[\]{}|\\`~!@#$%^&*_\-+=;:\'",<>./?]', password): raise ValidationError( _("The password must contain at least 1 symbol: " + "()[]{}|\`~!@#$%^&*_-+=;:'\",<>./?"), code='password_no_symbol', ) def get_help_text(self): return _( "Your password must contain at least 1 symbol: " + "()[]{}|\`~!@#$%^&*_-+=;:'\",<>./?" )
If you plan to reuse this code on different projects, you can make your validators more configurable through the Django settings. Then you can pass variables into your validator like the min_length
value sent to the MinimumLengthValidator
shown above.
Here I've adjusted my NumberValidator to have a configurable number of digits by adding the __init__
method and adjusting the validate
method to check the length of the number of matches:
class NumberValidator(object): def __init__(self, min_digits=0): self.min_digits = min_digits def validate(self, password, user=None): if not len(re.findall('\d', password)) >= self.min_digits: raise ValidationError( _("The password must contain at least %(min_digits)d digit(s), 0-9."), code='password_no_number', params={'min_digits': self.min_digits}, ) def get_help_text(self): return _( "Your password must contain at least %(min_digits)d digit(s), 0-9." % {'min_digits': self.min_digits} )
Finally, update the AUTH_PASSWORD_VALIDATORS setting with the correct dotted path to your validators:
AUTH_PASSWORD_VALIDATORS = [ {'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', 'OPTIONS': { 'min_length': 12, } }, {'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', }, {'NAME': 'my.project.validators.NumberValidator', 'OPTIONS': { 'min_digits': 3, }}, {'NAME': 'my.project.validators.UppercaseValidator', }, {'NAME': 'my.project.validators.LowercaseValidator', }, {'NAME': 'my.project.validators.SymbolValidator', }, ]
Using this technique you could create a single, highly-configurable validator covering these rules and/or others to reuse on all your projects as needed.
In the future, there is no reason to let your Django users gamble with weak passwords. Help them out by adding some stronger password requirements. Here's additional good documentation on Django's password validation.