Skip to content

django-pwned-passwords

Posted on:August 5, 2017

pwned.

On August 3rd, Troy Hunt released a new addition to the popular ’;—have i been pwned? service - a website that tells you if your user information such as an email address has been involved in a major security breach in the past.

Troy announced the addition of 306 million pwned passwords. This data set will allow the public to analyze new passwords to ensure they have not been involved in a similar breach to those reflected by the classic service.

pwned passwords matter

Some of the most popular attack vectors leverage lists of common passwords or dictionaries of common words and substitutions to identify a user’s password and gain malicious access to an account. For those malicious users, lists of passwords are a great resource, as many people use the same password for multiple services, or in the case of simple passwords like P@ssw0rd, the same password as each other.

The mere presence of this dataset screams security nightmare for users that secure their accounts with one of the passwords on that list. I don’t necessarily recommend typing your password into someone else’s website, but you can download the data set too and cross reference your personal data with that list.

That said, when creating new accounts, it’s important to ensure that these accounts are not using already-pwned passwords. To aid in this, Troy has released an API to determine if a password was found in the set.

Django integration

As an avid Django developer and a (junior) security enthusiast, I figured I’d build a small integration to allow Django user registrations to cross-reference that list of passwords.

For the last day or so, I’ve been working on django-pwned-passwords, a package that provides a Django password validator that will cross reference the password of new registrants with that list. You can read more over at Github or Read The Docs. For a quick installation:

Install django-pwned-passwords::

$ pip install django-pwned-passwords

Add it to your INSTALLED_APPS:

INSTALLED_APPS = (
    ...
    'django_pwned_passwords',
    ...
)

Add django-pwned-passwords’s PWNEDPasswordValidator:

AUTH_PASSWORD_VALIDATORS = [
    ...
    {
        'NAME': 'django_pwned_passwords.password_validation.PWNEDPasswordValidator'
    }
]

Password Sensitivity

This app currently sends information about user passwords to a third party. There are potential security risks associated with this practice.

As of version 2.0, the application uses the newer version of the API’s k-anonymity model so that only a portion of a hashed password is sent. This drastically reduces the risk of using this service, and has also allowed the API maintainer to remove the rate limiting because the results can now be cached across a CDN. Despite this major improvement, it’s important that any developer using django-pwned-passwords is aware of how this impacts them. For visibility, here is the current implementation as of version 2.0.0. The source code is available as well, so if you’re using a higher version and I haven’t udpated this page, you should reference that.

try:
    p_hash = hashlib.sha1(password.encode('utf-8')).hexdigest().upper()
    response = requests.get(self.get_url(p_hash[0:5]), timeout=self.timeout)
    if p_hash[5:] in response.text:
        return INVALID
    elif self.fail_safe:
        return VALID
    elif response.status_code in [400, 429, 500]:
        raise ValidationError(self.error_fail_msg)
except requests.exceptions.RequestException:
    if not self.fail_safe:
        raise ValidationError(self.error_fail_msg)
    return VALID

if self.fail_safe:
    return VALID
raise ValidationError(self.error_fail_msg)