-1

I've two rest endpoints, one that registers an user and the second that lets that user login. This is the code that I'm using to hash the user's password and store the hashed password in the database,

from passlib.hash import pbkdf2_sha256
def _get_hashed_password(self, password):
    return pbkdf2_sha256.encrypt(password, rounds=200000, salt_size=16)

Now on the login endpoint I use this code to verify the hashed password,

password = data['password']
hash = pbkdf2_sha256.encrypt(password, rounds=200000, salt_size=16)
pbkdf2_sha256.verify(user.hashed_password, hash)

This method fails and clearly because the two passwords are different,

$pbkdf2-sha256$200000$ai0FoDTG2BuDkDKGEIJQKg$Ik06dr61.2rRMDwZCZMdVq.zMe5887.ksDxvmSXFRwE
$pbkdf2-sha256$200000$pHTuHYNwLoXQeu8dI0QoxQ$2z4cZl9Njz9X/bxNtWCZzzeplWO.jTZA2v5lvcmgFE8

I'm wondering how can I get this to work?

Melissa Stewart
  • 3,483
  • 11
  • 49
  • 88
  • 2
    [Have you tried doing it like in the first example in the documentation?](https://passlib.readthedocs.io/en/stable/) –  Jan 07 '18 at 05:21
  • @Terminus you mean instead of the encrypt method use the hash method. Otherwise I think everything is exactly the same. – Melissa Stewart Jan 07 '18 at 05:24
  • 2
    In addition to what @Terminus said, hashing the same password twice will not give you the same results. Your "hash" actually contains multiple values delimited by dollar signs: `pbkdf2-sha256`, `200000`, `pHTuHYNwLoXQeu8dI0QoxQ`, and `2z4cZl9Njz9X/bxNtWCZzzeplWO.jTZA2v5lvcmgFE8`. The third value is a salt, which is randomly generated and affects the fourth value. – Blender Jan 07 '18 at 05:33
  • Previous comment had the wrong answer and so was deleted. And that is why I don't like to answer in the comments. –  Jan 07 '18 at 05:35
  • @Blender that's what I figured too, so how do I make it work, do I need to store the salt separately or how do I get this to work in two separate rest end points? – Melissa Stewart Jan 07 '18 at 05:37
  • I don't understand why is this question being downvoted? – Melissa Stewart Jan 07 '18 at 05:40
  • Where does `user.hashed_password` come from? Your database? –  Jan 07 '18 at 05:48
  • @Terminus yes that's the hashed_password stored in the database when the user registered. – Melissa Stewart Jan 07 '18 at 05:50
  • 1
    In that case, Blender's answer should be the answer you need. Please do read all of it. Happy Programming! –  Jan 07 '18 at 05:53

1 Answers1

7

pbkdf2_sha256.encrypt and pbkdf2_sha256.verify do more than just compute the hash of your password. Look closely at the output of pbkdf2_sha256.encrypt

$pbkdf2-sha256$200000$ai0FoDTG2BuDkDKGEIJQKg$Ik06dr61.2rRMDwZCZMdVq.zMe5887.ksDxvmSXFRwE
----------------------------------------------------------------------------------------
$  algorithm  $rounds$         salt         $             the actual hash

You can switch algorithms, increase the number of rounds in a few years, and pbkdf2_sha256.verify will still be able to verify a plaintext password against the hash stored in your database.

Hashing the same password twice will not give you the same result each time because a random salt is used. The salt prevents rainbow table attacks in the event an attacker gains access to your password hashes. Basically, instead of computing just hash(password), you compute something like hash(salt + password). Since the salt is already given in the above string and you presumably know the password, you will also know the value of salt + password and your life won't be any more difficult. But for an attacker that spent a lot of time pre-computing just hash(password), none of those hashes will be useful because they don't incorporate your random salt.

As in the example posted in the comments, you don't need to do anything special to verify your passwords:

pbkdf2_sha256.verify(password, user.hashed_password)

There's no need to call pbkdf2_sha256.encrypt when verifying a password. pbkdf2_sha256.verify does all of this for you.

Blender
  • 289,723
  • 53
  • 439
  • 496