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, andpwned-passwords-djangoprovides an additional validator which checks the Pwned Passwords database. To enable it, set yourAUTH_PASSWORD_VALIDATORSsetting 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-djangoneeds 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’sCommonPasswordValidator, which uses a smaller, locally-stored list of common passwords. Whenever this happens, a message of levellogging.ERRORwill 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
createsuperuserorchangepasswordmanagement 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_VALIDATORSsetting. You must be prepared to catchValidationErrorwhen 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",
)
}
},
]