74 lines
2.8 KiB
Plaintext

== How to fix it in Django
=== Code examples
==== Noncompliant code example
Django uses the first item in the `PASSWORD_HASHERS` list to store new passwords.
In this example, SHA-1 is used, which is too fast to store passwords.
[source,python,diff-id=203,diff-type=noncompliant]
----
# settings.py
PASSWORD_HASHERS = [
'django.contrib.auth.hashers.SHA1PasswordHasher', # Noncompliant
'django.contrib.auth.hashers.CryptPasswordHasher',
'django.contrib.auth.hashers.Argon2PasswordHasher',
'django.contrib.auth.hashers.ScryptPasswordHasher',
]
----
==== Compliant solution
This example requires `argon2-cffi` to be installed, which can be done using `pip install django[argon2]`.
[source,python,diff-id=203,diff-type=compliant]
----
# settings.py
PASSWORD_HASHERS = [
'django.contrib.auth.hashers.Argon2PasswordHasher',
'django.contrib.auth.hashers.ScryptPasswordHasher',
'django.contrib.auth.hashers.BCryptSHA256PasswordHasher',
'django.contrib.auth.hashers.PBKDF2PasswordHasher',
'django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher',
]
----
=== How does this work?
include::../../common/fix/password-hashing.adoc[]
In the previous example, Argon2 is used as the default password hashing function
by Django. Use the `PASSWORD_HASHERS` variable carefuly. If there is a need to
upgrade, use
https://docs.djangoproject.com/en/5.0/topics/auth/passwords/#password-upgrading[Django's password upgrade documentation].
=== Going the extra mile
==== Tweaking password hashing parameters
It is possible to change the parameters of the password hashing algorithm to
make it more secure. For example, you can increase the number of iterations or
the length of the salt. +
https://docs.djangoproject.com/en/5.0/topics/auth/passwords/[The Django documentation contains more details about these parameters].
==== Preventing user enumeration attacks
Django uses the first item in `PASSWORD_HASHERS` to store passwords, but uses every hashing algorithm in the `PASSWORD_HASHERS`
list to check passwords during user login. If a user password was not hashed using the first algorithm, then Django upgrades
the hashed password after a user logs in.
This process is convenient to keep users up to date, but is also vulnerable to enumeration. If an
attacker wants to know whether an account exists, they can attempt a login with that account. By
tracking how long it took to get a response, they can know if an older hashing algorithm was used
(so the account exists) or the new hashing algorithm was used (the default is an account does not
exist.)
To fix this, https://docs.djangoproject.com/en/5.0/topics/auth/passwords/#password-upgrading-without-requiring-a-login[the Django documentation]
defines how to upgrade passwords without needing to log in. In this case, a custom hasher has to
be created that wraps the old hash.
include::../../common/extra-mile/peppering.adoc[]