Skip to main content

Django hashers using passlib

Project description

django-hashers-passlib[-revived]

⚠️ The original project django-hashers-passlib has no longer been maintained. It was forked, updated and renamed to django-hashers-passlib-revived. You may still find the term django-hashers-passlib here and throughout the code.


django-hashers-passlib-revived aims to make password hashing schemes provided by passlib usable in Django. Unlike passlibs passlib.ext.django, it does not replace Django's password management system but provides standard hashers that can be added to the PASSWORD_HASHERS setting for hash schemes provided by passlib.

There are two primary use cases for this module:

  1. You want to import password hashes from an existing application into your Django database.
  2. You want to export password hashes to a different application in the future.

Installation

This module is available via pip, install it with

pip install django-hashers-passlib-revived

Compatibility Matrix:

Py/Dj 3.8 3.9 3.10 3.11 3.12
3.2 (LTS)
4.0
4.1
4.2 (LTS)
5.0

In addition, passlib>=1.7" is a dependency.

Getting started

This module supports almost every hash supported by passlib (some must be converted at first - see below). If you want your Django project app to understand hashes provided by passlib, simply add the hashers to the PASSWORD_HASHERS setting. Note that the first value is the default hasher, so if you want to store new user passwords in one of these hashes, prepend the hash to the list:

PASSWORD_HASHERS = [
    # new user passwords should be stored in the phpass format
    'hashers_passlib.phpass',

    'django.contrib.auth.hashers.PBKDF2PasswordHasher',
    # ... other Django hashers

    # We also want to add some users from say mssql2000 (who wouldn't?)
    'hashers_passlib.mssql2000',
]

Almost every module in passlib has hasher with the same name, see "Supported hashes" below for full list.

You can also configure default parameters for different hash algorithms, for example to configure a different number of rounds for pbkdf2_sha256:

PASSLIB_KEYWORDS = {
    'pbkdf2_sha256': {
        'rounds': 32000,
    },
}

The documentation for passlib contains a list of available parameters.

Import/Export

Django dictates a scheme for storing passwords (see How Django stores passwords. Some hashes are stored simply by prefixing the hash name, others already almost fit into the scheme and only their leading $ is stripped.

If you want to import hashes from another application into Djangos hash encoding scheme (see "How it works interally" below for details), every hasher has a from_orig() and to_orig() method, which allows to import/export hashes. So importing a user from a different system is simply a matter of calling from_orig() of the right hasher and save that to the password field of Djangos User model. Here is a simple example:

# Lets import a phpass (WordPress, phpBB3, ...) hash. This assumes that you have 'hashers_passlib.phpass' in
# your PASSWORD_HASHERS setting.

from django.contrib.auth import get_user_model
from django.contrib.auth.hashers import get_hasher

User = get_user_model()  # get any custom user model

hasher = get_hasher('phpass')

# you got this from i.e. a WordPress database:
raw_hashes = {
    'joe': '$P$EnOjUf5ie1AeWMHpw1dqHUQYHAIBe41',
    'jane': '$P$E6UROQJscRzZ3ve2hoIFZ1OcjBA1W10',
}

for username, hash in raw_hashes.items():
    user = User.objects.create(username=username)
    user.password = hasher.from_orig(hash)
    user.save()

The users "joe" and "jane" can now login with their old usernames and passwords.

If you want to export users with a phpass hash to a WordPress database again, you can simple get the original hashes back (for simplicity, we just print everything to stdout here):

from django.contrib.auth import get_user_model
from django.contrib.auth.hashers import get_hasher

User = get_user_model()  # get any custom user model

hasher = get_hasher('phpass')

for user in User.objects.filter(password__startswith='phpass$'):
    orig_hash = hasher.to_orig(user.password)
    print('%s has hash "%s"' % (user.username, orig_hash))

Supported hashes

This module provides hashers for most hash schemes provided by passlib - but remember you have to import them using the hashers from_orig() method first to be useable. Some have to be be converted first (see below), and only a few minor old hashes are not supported. All password hashers have the same class name as the passlib hasher they wrap and are located in the hashers_passlib module. So to enable support for e.g. sha1_crypt hashes, add hashers_passlib.sha1_crypt to your PASSWORD_HASHERS Django setting.

WARNING: Some hashes are longer then the 128 characters provided by the standard User model provided by Django. You have to specify a custom user model with at least 256 characters for hex_sha512, pbkdf2_sha512, scram and sha512_crypt or at least 384 characters for grub_pbkdf2_sha512.

The following algorithms are supported: des_crypt, bsdi_crypt, bigcrypt, crypt16, md5_crypt, sha1_crypt, sun_md5_crypt, sha256_crypt, sha512_crypt, apr_md5_crypt, bcrypt_sha256, phpass, pbkdf2_<digest>, dlitz_pbkdf2_sha1, cta_pbkdf2_sha1, scram, ldap_salted_md5, ldap_salted_sha1, atlassian_pbkdf2_sha1, fshp, mssql2000, mssql2005, mysql323, mysql41, oracle11, lmhash, nthash, cisco_pix, cisco_type7, grub_pbkdf2_sha512, hex_{md4,sha256,sha512}, argon2, and scrypt

Most hashes will be saved with a simple prefix <algorithm>$, where "<algorithm>" is the name of the hasher. The only exception are a few hashes (apr_md5_crypt, bcrypt_sha256, pbkdf2_<digest>, scram) that already almost fit into Djangos hash scheme, where only the leading $ is stripped.

NOTE: Some hashes (bcrypt_sha256, pbkdf2_<digest>, ...) look very similar to what Django provides but are actually distinct algorithms.

Hashes supported via conversion

Some hash schemes really are just a minor transformation of a different hash scheme. For example, the bsd_nthash is just a regular nthash with $3$$ prepended and the ldap_md5 has is just a plain MD5 hash with {MD5} prepended that is already supported by Django.

In order to avoid code duplication, this module does not provide password hashers for these schemes, but converters under hashers_passlib.converters. Converted hashes are either readable by a different hasher or by a hasher provided by Django.

If you want to import bsd_nthash hashes, you can either manually strip the identifier or use the converter:

# Lets import bsd_nthash hashes as plain nthash hashes. This assumes you have
# have 'hashers_passlib.nthash' in your PASSWORD_HASHERS setting.

from django.contrib.auth import get_user_model
from django.contrib.auth.hashers import get_hasher

from hashers_passlib.converters import bsd_nthash

conv = bsd_nthash()

raw_hashes = {
    'joe': '$3$$baac3929fabc9e6dcd32421ba94a84d4',
}

for username, hash in raw_hashes.items():
    user = User.objects.create(username=username)

    # convert bsd_nthash to plain nthash:
    user.password = converter.from_orig(hash)
    user.save()

The following converters are available under hashers_passlib.converters, they can be used to convert from and to the original scheme:

From To Notes
bcrypt BCryptPasswordHasher Converted to bcrypt hash supported by the stock Django hasher.
bsd_nthash nthash Convert from bsd_nthash to nthash and vice versa.
ldap_md5 UnsaltedMD5PasswordHasher Converted to plain MD5 hash supported by Django.
ldap_sha1 UnsaltedSHA1PasswordHasher Converted to plain SHA1 hash supported by Django.
ldap_hex_md5 UnsaltedMD5PasswordHasher Converted to plain MD5 hash supported by Django.
ldap_hex_sha1 UnsaltedSHA1PasswordHasher Converted to plain SHA1 hash supported by Django.
ldap_{crypt} various Converted to their non-LDAP pendants (i.e. ldap_des_crypt is converted to a plain des_crypt hash).
ldap_bcrypt BCryptPasswordHasher Unlike other ldap_{crypt} schemes, ldap_bcrypt hashes are converted to what Djangos stock BCrypt hashser understands.
ldap_pbkdf2_{digest} pbkdf2_{digest} Converted to their non-LDAP pendants.

Unsupported hashes

Some hashes are unsupported because they require the username to generate the salt: postgres_md5, oracle10, msdcc and msdcc2.

How it works internally

Djangos password management system stores passwords in a format that is very similar but still distinct from what passlib calls Modular Crypt Format:

<algorithm>$<content>

... where "<algorithm>" is the identifier used to select what hasher class should handle the hash. The only difference to the Modular Crypt Format is that it misses the leading $ sign. Note that the $ in the middle is a mandatory delimiter.

This module modifies the hash schemes so they fit into this scheme before storing them in the database. The modifications are absolutely reversible - in fact this module depends on it being reversible, our hashers won't work any other way. Depending on the original hash scheme, the hashes are modified in one of several ways:

  1. Some old and insecure hashes require the username to encode the hash. Djangos hashers don't receive the username, so they are not compatible and not supported by this module.
  2. Some of passlibs hashes are already supported by Django and the functionality is not duplicated here.
  3. Some hash schemes are really just minor modifications of different schemes, we provide converters in this case.
  4. A few hashes already almost fit in Djangos scheme and have a reasonably unique identifier, they just have the leading $ stripped.
  5. All other hashes (which is the vast majority!) just have <identifier>$ prepended. This is the same approach as what Django does with e.g. bcrypt hashes.

Local Development

Install the project and its development dependencies with Poetry:

pip install poetry
poetry install

You can invoke the Testsuite with pytest:

poetry run pytest

You can test against a combination of all supported Django and Python versions with Tox:

pip install tox
tox p

ChangeLog

2.0 (WIP)

The original project django-hashes-passlib has no longer been maintained by the original developer. It was forked and renamed to django-hashers-passlib-revived

  • Added support for Django 4.1, 4.2 and 5.0.
  • Added support for Python 3.11 and 3.12.
  • Added Types for everything.
  • Use Poetry for a local developme environment.
  • Overall code cleanup and modernization.

1.0.0 (never released)

  • Add support for Django 3.2 and Django 4.0.
  • Add support for Python 3.7 - 3.10.
  • Drop support for long out-of-date Django versions 1.8, 1.10 and 1.11.
  • Drop support for deprecated versions of Python (2.7 - 3.6).
  • Add argon2d and argon2id hashers. We recommend that you add all argon2 hashers if needed.
  • Switch to GitHub Actions for CI testing.
  • Modernize project setup (pyproject.toml, setup.cfg, etc).
  • Add black and pylint to suite of linters/formatters.

0.4 (19 November 2017)

  • Support passlib 1.7.
  • Add argon2 and scrypt hashers.
  • Make hash parameters configurable via the PASSLIB_KEYWORDS setting.
  • Add VERSION and get_version() similar to Django.
  • Integrate with Travis to run test-suite with Python 2.7, 3.4+ and all currently supported versions of Django.
  • Update python version classifiers in setup.py.
  • Also upload wheels.

0.3 (05 December 2015)

  • Require Python3>=3.4.
  • Depend on Django>=1.8.
  • Update passlib and bcrypt dependencies.

0.2 (22 March 2014)

  • Remove distribute_setup.py.
  • Implement a generic safe_summary() method for all hashers.
  • Add bcrypt to requirements.txt.
  • Version reported to setup.py is now the same as git describe if executed from a git repository.
  • Add a version setup.py command.
  • Fix ldap_md5 and ldap_sha1 converters in Python 3.

0.1 (01 January 2014)

  • Initial release.

Project details


Release history Release notifications | RSS feed

This version

2.0

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

django_hashers_passlib_revived-2.0.tar.gz (29.6 kB view details)

Uploaded Source

Built Distribution

File details

Details for the file django_hashers_passlib_revived-2.0.tar.gz.

File metadata

File hashes

Hashes for django_hashers_passlib_revived-2.0.tar.gz
Algorithm Hash digest
SHA256 530904d2072db492f00565e351356e9e8409a2059b3c18f7cd1c239a9920d355
MD5 09ed55b9de29803fcae6b40876f90f9e
BLAKE2b-256 0191bb2708b929e9d7f9894c920752eea48943e4c88c4c8d5d5d09b8505e5e15

See more details on using hashes here.

File details

Details for the file django_hashers_passlib_revived-2.0-py3-none-any.whl.

File metadata

File hashes

Hashes for django_hashers_passlib_revived-2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 7656ce47fb0fecb89157f8c7790a93acec28226360c533a42dedb72c706849c0
MD5 079bfad7e77b3270abe9eedf527dcb06
BLAKE2b-256 712ab792e9c73a7eeb3945b3b099f7f3cd7eb4166abbe7b20c4bbc926a3dbab6

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page