Using the password validator#
- class pwned_passwords_django.validators.PwnedPasswordsValidator[source]#
Django’s auth system (located in
django.contrib.auth
) includes a configurable password-validation framework with several built-in validators, and pwned-passwords-django provides an additional validator which checks the Pwned Passwords database. To enable it, set yourAUTH_PASSWORD_VALIDATORS
setting to include the new validator, like so:AUTH_PASSWORD_VALIDATORS = [ # ... other password validators ... { "NAME": "pwned_passwords_django.validators.PwnedPasswordsValidator", }, ]
This will cause most high-level password-setting operations to check the Pwned Passwords database, and reject any password found there.
Warning
API failures
pwned-passwords-django needs to communicate with the Pwned Passwords API in order to check passwords. If Pwned Passwords is down or timing out (the default connection timeout is 1 second), or if any other error occurs when checking the password, this validator will fall back to using Django’s
CommonPasswordValidator
, which uses a smaller, locally-stored list of common passwords. Whenever this happens, a message of levellogging.ERROR
will appear in your logs, indicating what type of failure was encountered in talking to the Pwned Passwords API.See the error-handling documentation for details.
This validator implements the following standard Django password-validator method. It will be called automatically by Django at appropriate times, and you should not ever need to call it yourself unless you are performing low-level/manual password changes (not recommended):
- validate(password: str, user: AbstractBaseUser | None = None)[source]#
Check a proposed password against Pwned Passwords and reject the password if it has been compromised.
This method is called by most high-level account-creation and account-editing operations in Django.
- Raises:
django.core.exceptions.ValidationError – when the proposed password is compromised.
Limitations#
As mentioned above, Django automatically runs password validators on certain high-level operations. These operations are:
Whenever a user changes or resets their password with Django’s built-in auth views and forms.
Whenever a new user is created via Django’s built-in
UserCreationForm
.Whenever the
createsuperuser
orchangepassword
management commands are used.
Password validators are not run automatically when a user’s password is set or updated via other mechanisms, and in those cases the validator cannot provide automatic protection against a user choosing a breached password.
Thus the best approach is always to use the built-in Django auth components, and avoid setting/updating passwords via other mechanisms. But if you must work manually with passwords, it is recommended that you do the following:
Prior to manually setting or changing a password, call
django.contrib.auth.password_validation.validate_password()
on the proposed new password. This will manually run all password validators configured in yourAUTH_PASSWORD_VALIDATORS
setting. You must be prepared to catchValidationError
when doing so.Then use the
set_password()
method of the user model to set the password after successfully validating it.
It is also strongly recommended that you enable the middleware provided
by pwned-passwords-django, which provides a way to check every
incoming HTTP POST
payload for potentially-compromised passwords.
Customizing the validator#
To change the error or help messages shown to the user, you can pass
OPTIONS
when adding the validator to your settings:
AUTH_PASSWORD_VALIDATORS = [
# ... other password validators ...
{
"NAME": "pwned_passwords_django.validators.PwnedPasswordsValidator",
"OPTIONS": {
"error_message": "That password was pwned",
"help_message": "Your password can't be a commonly used password.",
}
},
]
The number of times the password has appeared in a breach can also be included in the error message, including a plural form:
AUTH_PASSWORD_VALIDATORS = [
# ... other password validators ...
{
"NAME": "pwned_passwords_django.validators.PwnedPasswordsValidator",
"OPTIONS": {
"error_message": (
"Pwned %(amount)d time",
"Pwned %(amount)d times",
)
}
},
]