Enhancing Django's Password Validators with Custom Solutions
Written on
Welcome to Django Shorts
Django comes equipped with a decent array of password validators by default, but you might want to create your own. Fortunately, this process is straightforward!
In this tutorial series, we aim to provide quick and effective solutions tailored to various requirements within your Django projects, while enhancing your expertise.
Django is designed for perfectionists working against deadlines, so let’s refine our password validators.
Prerequisites This article assumes you have prior experience with Django projects.
Understanding Password Validators
Password validators are snippets of code that verify whether a password fulfills specific criteria. Their primary purpose is to ensure that passwords are not mere sequences of characters easily subjected to brute-force or dictionary attacks.
Common rules for password validation include: - At least one digit - At least one uppercase letter - At least one lowercase letter - At least one special character
Built-in Password Validators in Django
Django includes a range of built-in password validators that enforce the following rules: - Password similarity to the username - Minimum password length - Passwords that match commonly used passwords (20,000 records) - Passwords that are entirely numeric
To utilize these validators, you must specify them in the settings.py file:
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',},
]
Some validators allow configuration options; for instance, the minimum length validator specifies a password length (default is 8 characters).
Error messages and guidance text are presented to the user in the order defined in the settings.
Creating Simple Custom Password Validators
Developing a custom password validator for Django is quite simple. You just need to create a class that implements two methods: - validate: This method checks the password against the specified rules and either returns None (indicating success) or raises a ValidationError (if validation fails). - get_help_text: This method returns the guidance text displayed to the user.
Here’s an example of a custom validator that checks for at least one digit (using regex):
class NumberValidator(object):
def validate(self, password, user=None):
if not re.findall(r'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.")
You can create additional custom validators by following this pattern. Let’s examine a complete example of custom validators, which we will place in a separate file named validators.py.
Walkthrough of the Custom Validators Code - Line 6 defines a NumberValidator that checks for at least one digit. - Line 20 introduces an UppercaseValidator that ensures the password contains at least one uppercase letter. - Line 34 adds a SymbolValidator that validates for at least one special character.
Each validator implements a straightforward regex rule to check and validate the specified criteria.
To use your new custom validators, they must be added to the settings.py file:
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',},
{
'NAME': 'password_validators.validators.NumberValidator',},
{
'NAME': 'password_validators.validators.UppercaseValidator',},
{
'NAME': 'password_validators.validators.SymbolValidator',},
]
Let’s see these validators in action.
You can log into the Django Admin dashboard and attempt to change a user's password using the standard form.
Accessing Model Data with Custom Password Validators A common requirement for password validation is checking if a new password is identical to any that the user has previously used. This requires accessing stored data, necessitating a model definition to read and write hashed passwords securely.
Let’s begin by defining our StoredPassword model in models.py:
from django.conf import settings
from django.db import models
class StoredPassword(models.Model):
user = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
editable=False
)
password = models.CharField(
'Password hash',
max_length=255,
editable=False
)
date = models.DateTimeField(
'Date',
auto_now_add=True,
editable=False
)
This model definition is quite simple, storing the user, hashed password, and the creation date for reference.
Since we now have a model, we must ensure that the application defining the model is included in the settings.py file:
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'password_validators',
]
Also, remember to create and run the database migrations:
$ python3 manage.py makemigrations
$ python3 manage.py migrate
With the model in place to store previous passwords, we can now add the new custom password validator:
Code Walkthrough - Line 7 defines the SALT used to hash the password stored in our model. - Line 53 implements the repeated password validator, which includes defining the validate method and the password_changed method (triggered after a successful password change) to store the passwords. - Lines 59-61 hash the password using the SALT and check if an identical record (user/password combination) exists in the database. - Lines 72-78 hash the password and verify if the record already exists in the database (which it shouldn't) and then store it in the StoredPassword model.
If you attempt to change the password to a previously used one, you will see the following error:
Note that you must change the password twice to ensure a prior password matches the rule.
Conclusion
As demonstrated, implementing password validators in Django is quite simple and allows for customization of password validation rules, which is essential for compliance with certain regulations.
Explore the complete source code on GitHub at: https://github.com/nunombispo/DjangoShorts-PasswordValidators
Discover more Python/Django shorts:
Python Shorts — NLP Sentiment Analysis and Text Summary
What if you could perform sentiment analysis and create text summaries using Python and NLP without relying on costly APIs?
Django Shorts — User Authentication and Lockout
Django already offers robust user security, but what if you want to implement account lockout?
Follow me on Twitter: https://twitter.com/DevAsService Visit my website: https://developer-service.io/
If you enjoyed this article and found it helpful, consider supporting me by signing up for a Medium membership (if you aren’t a member yet). It costs just $5 a month and grants you access to all stories on Medium! (I will receive a small commission).
To stay updated on my latest posts, feel free to sign up for my free newsletter!