Opened 18 months ago

Closed 17 months ago

Last modified 17 months ago

#34661 closed New feature (duplicate)

Peppering user passwords

Reported by: Fatih Erikli Owned by: nobody
Component: contrib.auth Version: 4.2
Severity: Normal Keywords:
Cc: Triage Stage: Unreviewed
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description (last modified by Fatih Erikli)

Currently a user's password stored in database in this format:

<algorithm>$<iterations>$<salt>$<hash>

Hash (Last column) is the password hashed with the salt in previous column.

Example, these are two computed passwords, stored on database, for the same password of two users.

pbkdf2_sha256$600000$fb9cUsHWK4EMZ7VWGBAcGD$2uwiVefFwanIhxLrv+/t3sKvP4X6tDKEMw/ysHD5dIc=
pbkdf2_sha256$600000$HgWHWrF2qQD9Owj4XeEkjY$rh0qzfo+/ZCzWbL9ZJa8aKhiO5xoEMfT4EtP/+A+LzI=

The password is 123456.

Imagine I am an attacker, who got the database of different django project, I want to look up the users who have chosen the password 123456.

I have the salts of users stored in database.

make_password('123456', 'fb9cUsHWK4EMZ7VWGBAcGD')
pbkdf2_sha256$600000$fb9cUsHWK4EMZ7VWGBAcGD$2uwiVefFwanIhxLrv+/t3sKvP4X6tDKEMw/ysHD5dIc=

make_password('123456', 'HgWHWrF2qQD9Owj4XeEkjY')
pbkdf2_sha256$600000$HgWHWrF2qQD9Owj4XeEkjY$rh0qzfo+/ZCzWbL9ZJa8aKhiO5xoEMfT4EtP/+A+LzI=

These are correct password combinations. I am able to lookup the users who have their passwords exposed in public.

Password 123456 is not a possible case in Django, since the password fields have a complexity validation. However, the salt is available to the attacker when a database is stolen. Salt could be used

  • to hash the raw password pair in a rainbow table.
  • to hash the already exposed passwords.

There is one more element needed for hashing the password, pepper, should be project specific. When a database is exposed in public, the attacker will not be able to lookup the passwords, since they don't have the secret pepper key.

I am not sure about the vulnerability enumeration, but CWE-760 seems closer. Salt is not weak, but it is known. The salt is stored next to the hashed password.

This is a case of when a database is stolen, however I think Django, by default, should do everything that could be done at the framework level to keep the user information secured.

Change History (14)

comment:1 by Fatih Erikli, 18 months ago

Description: modified (diff)

comment:2 by Fatih Erikli, 18 months ago

Description: modified (diff)

comment:3 by Fatih Erikli, 18 months ago

Description: modified (diff)

comment:4 by Fatih Erikli, 18 months ago

Description: modified (diff)

comment:5 by Fatih Erikli, 18 months ago

Description: modified (diff)

comment:6 by Fatih Erikli, 18 months ago

Description: modified (diff)

comment:7 by Fatih Erikli, 18 months ago

Description: modified (diff)

comment:8 by Fatih Erikli, 17 months ago

Here is an example hasher:

# yourapp.hashers.py
class PepperedPBKDF2PasswordHasher(PBKDF2PasswordHasher):
    algorithm = "pepperred_pbkdf2_sha256"

    def encode(self, password, salt, iterations=None):
        iterations = iterations or self.iterations
        hash = pbkdf2(password, salt + settings.PASSWORD_PEPPER, iterations, digest=self.digest)
        hash = base64.b64encode(hash).decode('ascii').strip()
        return "%s$%d$%s$%s" % (self.algorithm, iterations, salt, hash)

In settings:

PASSWORD_PEPPER = b'4545randombytes342445'
PASSWORD_HASHERS = [
    "yourapp.hashers.PepperedPBKDF2PasswordHasher",
    "django.contrib.auth.hashers.PBKDF2PasswordHasher",
    "django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher",
    "django.contrib.auth.hashers.Argon2PasswordHasher",
    "django.contrib.auth.hashers.BCryptSHA256PasswordHasher",
    "django.contrib.auth.hashers.ScryptPasswordHasher",
]

Passwords will continue working when the last (first in the list) hasher is added. Django updates the computed passwords when a new hashing algorithm is added. The passwords will break when a pepper is changed.

Version 9, edited 17 months ago by Fatih Erikli (previous) (next) (diff)

comment:9 by Fatih Erikli, 17 months ago

Description: modified (diff)

comment:10 by Fatih Erikli, 17 months ago

Description: modified (diff)

comment:11 by Fatih Erikli, 17 months ago

Description: modified (diff)

comment:12 by Fatih Erikli, 17 months ago

Description: modified (diff)

comment:13 by Mariusz Felisiak, 17 months ago

Resolution: duplicate
Status: newclosed

Duplicate of #30561.

comment:14 by Mariusz Felisiak, 17 months ago

Type: UncategorizedNew feature
Note: See TracTickets for help on using tickets.
Back to Top