Search code examples
pythondjangohashpasswordspbkdf2

Django password hashing different from python passlib pbkdf2 library


Using django admin, I have created a user

email: [email protected]
password: ccpass!ccpass

The hashed password stored in the database is

pbkdf2_sha256$260000$alGB1h2BRHwn83nz9fSJ3V$qippfbL8g59KPoDh+cIEh70TQCjuWeH8017VcLLpDIY=

All I know is that django is generating the password hash using PBKDF2 algorithm.

I need to run a pure python script that inside a part of it, it checks if a password is matching a hash. I have found a library passlib. I can provide the password and rounds. But where does the django salt come from?

from passlib.hash import pbkdf2_sha256

password = 'ccpass!ccpass'
db_hashed_password = 'pbkdf2_sha256$260000$alGB1h2BRHwn83nz9fSJ3V$qippfbL8g59KPoDh+cIEh70TQCjuWeH8017VcLLpDIY='

salt = db_hashed_password.split('$')[2].encode()
pbkdf2_sha256.hash(password, rounds=260000, salt=salt)

result:

$pbkdf2-sha256$260000$YWxHQjFoMkJSSHduODNuejlmU0ozVg$qippfbL8g59KPoDh.cIEh70TQCjuWeH8017VcLLpDIY

The resulted password hash apparently looks the same except minor differences.

The pbkdf2-sha256 has a dash instead of an underscore but this is not a problem. I can fix it.

The main problem is that I do receive a . instead of + in the actual hash part. How to fix this? Can I blindly replace all . with + ?

I also do not understand why is the calculated salt different from the original salt?


Solution

  • I followed the django source code.

    Having a look at encode function and pbkdf2 function, I can write the following code:

    import base64
    import hashlib
    
    myhash = base64.b64encode(hashlib.pbkdf2_hmac('sha256', password.encode(), salt, 260000, None)).decode("ascii").strip()
    
    print("%s$%d$%s$%s" % ('pbkdf2_sha256', 260000, salt.decode(), myhash))
    

    This pure python code gave me the same result as django:

    pbkdf2_sha256$260000$alGB1h2BRHwn83nz9fSJ3V$qippfbL8g59KPoDh+cIEh70TQCjuWeH8017VcLLpDIY=
    

    .