Using the middleware#
To help with situations where a potentially-compromised password is used
in ways Django’s password validators won’t catch, pwned-passwords-django
also provides a middleware
which monitors every incoming HTTP request for POST
payloads which appear
to contain passwords, and checks them against Pwned Passwords.
- pwned_passwords_django.middleware.pwned_passwords_middleware(get_response: Callable) Callable [source]#
Factory function returning a middleware – sync or async as necessary – which checks
POST
submissions that potentially contain passwords against the Pwned Passwords database.To enable the middleware, add
"pwned_passwords_django.middleware.pwned_passwords_middleware"
to yourMIDDLEWARE
setting. This will add a new attribute –pwned_passwords
– to eachHttpRequest
object. Therequest.pwned_passwords
attribute will be alist
ofstr
.Warning
Middleware order
The order of middleware classes in the Django
MIDDLEWARE
setting can be sensitive. In particular, any middlewares which affect file upload handlers must be listed above middlewares which inspectPOST
. Since this middleware has to inspectPOST
for likely passwords, it must be listed after any middlewares which might change upload handlers. If you’re unsure what this means, just put this middleware at the bottom of yourMIDDLEWARE
list.The
request.pwned_passwords
list will be empty if any of the following is true:The request method is not
POST
.The request method is
POST
, but the payload does not appear to contain a password.The request method is
POST
, and the payload appears to contain one or more passwords, but none were listed as compromised in Pwned Passwords.
If the request method is
POST
, and the payload appears to contain one or more passwords, and at least one of those is listed in Pwned Passwords, thenrequest.pwned_passwords
will be a list of keys fromrequest.POST
that contained compromised passwords.For example, if
request.POST
contains a key namedpassword_field
, andrequest.POST["password_field"]
is a password that appears in the Pwned Passwords database,request.pwned_passwords
will be["password_field"]
.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 middleware will fall back to using Django’sCommonPasswordValidator
, 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.
Here’s an example of how you might use Django’s message framework to indicate to a user that they’ve just submitted a password that appears to be compromised:
from django.contrib import messages def some_view(request): if request.method == "POST" and request.pwned_passwords: messages.warning( request, "You just entered a password which appears to be compromised!" )
pwned-passwords-django
uses a regular expression to guess which items inPOST
are likely to be passwords. By default, it matches on any key inPOST
containing"PASS"
(case-insensitive), which catches input names like"password"
,"passphrase"
, and so on. If you use something significantly different than this for a password input name, specify it – as a string, not as a compiled regex object! – in the settingsettings.PWNED_PASSWORDS["PASSWORD_REGEX"]
to tell the middleware what to look for. See the settings documentation for details.