1

I have an old database where user passwords were hashed with md5 without salt. Now I am converting the project into django and need to update passwords without asking users to log in.

I wrote this hasher:

from django.contrib.auth.hashers import PBKDF2PasswordHasher

class PBKDF2WrappedMD5PasswordHasher(PBKDF2PasswordHasher):
    algorithm = 'pbkdf2_wrapped_md5'

    def encode_md5_hash(self, md5_hash, salt):
        return super().encode(md5_hash, salt) 

and converting password like:

for data in old_user_data:
    hasher = PBKDF2WrappedMD5PasswordHasher()
    random_salt = get_random_string(length=8)
    # data['password'] is e.g. '972131D979FF69F96DDFCC7AE3769B31'
    user.password = hasher.encode_md5_hash(data['password'], random_salt)

but I can't login with my test-user.

any ideas? :/

doniyor
  • 36,596
  • 57
  • 175
  • 260
  • You are missing an `encode` method that will calculate the md5 hash of the password and then call `encode_md5_hash` with the result. See the [example in the docs](https://docs.djangoproject.com/en/1.11/topics/auth/passwords/#password-upgrading-without-requiring-a-login) for a wrapped SHA1 hasher. – Alasdair May 15 '19 at 09:08
  • @Alasdair right but that `encode()` is only called if user logs in right? – doniyor May 15 '19 at 09:11
  • I only have the md5 hashes - no raw password – doniyor May 15 '19 at 09:11
  • 1
    You don't need the raw password: the user supplies the raw password, you encode it to the md5 hash, then encode it to the pbkdf2 hash. You need a second step in your code. – 9769953 May 15 '19 at 09:14
  • Yes, the `encode()` method is called when the user logs in. – Alasdair May 15 '19 at 09:15
  • @Alasdair I am trying to achieve this logic: `972131D979FF69F96DDFCC7AE3769B31` --> `pbkdf2('972131D979FF69F96DDFCC7AE3769B31')` ---> `user can login`. – doniyor May 15 '19 at 09:20
  • 2
    When the user logs in, they give you the raw password, not the hash, so you need an encode method that does `raw_password -> md5(password) -> pbkdf2(md5(password))`. If my explanation doesn't help, then go over the example in the docs I linked to - it's doing the same thing except it's starting with SHA1 instead of md5. – Alasdair May 15 '19 at 09:28
  • @Alasdair yea I know :/ I already tried that and no luck so far... – doniyor May 15 '19 at 09:50

1 Answers1

1

I'm afraid you cannot do what you want with this. Hashing is strictly one-way, so there is no way to convert from one hash to another. You WILL have to update these passwords to the new hash one-by-one as users log in.

A decent strategy for implementing this change is:

  1. Mark all of your existing hashes as md5. You can just use some kind of boolean flag/column, but there is an accepted standard for this: https://passlib.readthedocs.io/en/stable/modular_crypt_format.html
  2. When the user logs in, authenticate them by first checking which type of hash they have, and then calculating that hash. If they are still md5, calculate the md5 to log them in; if they are now using pbkdf2, calculate that hash instead.
  3. After authenticating the password, if they are still flagged as md5, calculate the new format hash and replace it - making sure to now flag this as pbkdf2.

IMPORTANT: You will want to test this thoroughly before you release it to the wild. If you make a mistake, you might destroy the credentials of any user logging in. I would recommend temporarily retaining a copy of the old md5 hashes until you confirm production is stable, but make absolutely certain you destroy this copy completely. Your users passwords are not safe as long as the md5 hashes exist whatsoever.

Brad S
  • 186
  • 4