2

I am currently working on implementing a website that allows a user to (fictionally) buy and sell stocks. This is problem set 9 in Harvards' CS50 computer science course. My program compiles, functions and passes all checks. However, the past few days I've been stuck trying to implement a password change option.

On the password change page, I prompt the user to input their old password. Then, the user has to input their desired new password. Finally, the user has to confirm their new password once more.

However, my hashing function seems to output a different hash when a user inputs their old password, than it did when they registered with this password. This causes my comparison check between the hashed password in the database and the user input to always return false.

Below is the python code for the implementation of the password change. Below it is the html code for the actual page on which the user can change their password.

@app.route("/change", methods=["GET", "POST"])
@login_required
# PERSONAL TOUCH: <Allows user to change their password>
def change():
"""Allows user to change their password"""

if request.method == "GET":
    return render_template("change.html")

else:

    # Returns an error if Password is left blank
    if not request.form.get("originalpassword"):
        return apology("User must submit their original Password.", 400)

    elif not request.form.get("newpassword"):
        return apology("User must submit a new Password.", 400)

    elif not request.form.get("newconfirmation"):
        return apology("User must confirm their new Password", 400)

    # Hashes the Password
    Password = request.form.get("originalpassword")
    print("GIVEN PASSWORD: ", Password)
    HashedPassword = generate_password_hash(Password, method='pbkdf2:sha256', salt_length=8)

    # Returns an error if the user typed in a non-valid original password
    OldHashedPassword = db.execute("SELECT hash FROM users WHERE id = :id", id=session["user_id"])
    Old_HPW = OldHashedPassword[0]["hash"]

    print("given hash: ", HashedPassword)
    print("actual hash: ", Old_HPW)

    print("=====\n",OldHashedPassword,"\n=====\n")

    if not Old_HPW == HashedPassword:
        return apology("Submitted password is not valid.")

    # Returns an error if Password and Confirmation are not identical
    if not request.form.get("newpassword") == request.form.get("confirmpassword"):
        return apology("New Password and Confirmation must be identical.", 400)

    # Hashes the new Password
    NewPassword = request.form.get("newpassword")
    NewHashedPassword = generate_password_hash(NewPassword, method='pbkdf2:sha256', salt_length=8)

    # Insert the new Password into the database
    insertPW = db.execute("INSERT INTO users (hash) VALUES(:hash)", hash=NewHashedPassword)

    return redirect("/")

And the HTML code:

HTML Code

I did include a few print statements in order to debug. I registered a new account with username 'q', and password 'q'. Then, I attempted to change the password into 'qq'. During registration, the input 'q' resulted in the following hash:

pbkdf2:sha256:50000$sBxqbmza$e35dd4e61eb186af014e5e1ce3b73450b1361baabdd2f5f4559ad83ef0d5b45b

However, when I input 'q' into the 'original password' form field, the generate_password__hash function returns the hash:

pbkdf2:sha256:50000$iAjKAPM1$f7644f34f21864062efa2f3e3f3ea0a89a8a3391a0223c1a62fa7cbaab012a71

The values printed by the print statements are:

GIVEN PASSWORD: q

given hash:

pbkdf2:sha256:50000$iAjKAPM1$f7644f34f21864062efa2f3e3f3ea0a89a8a3391a0223c1a62fa7cbaab012a71

actual hash:

pbkdf2:sha256:50000$sBxqbmza$e35dd4e61eb186af014e5e1ce3b73450b1361baabdd2f5f4559ad83ef0d5b45b

Apologies for the very long post. Does anyone have an idea what is causing the hash function to output a different hash value for a (seemingly) similar input?

Thanks!

EDIT: this new code seems to have solved the problem:

NEW CODE

  • 3
    Where is your `generate_password_hash` function? In any case, the hash is salted - so it will be different each time. – Adam Dec 16 '18 at 16:01
  • 2
    I think it must be a salt problem. See [generate_password_hash](http://werkzeug.pocoo.org/docs/0.14/utils/) The salt is generated by the function. Also see check_password_hash – kelalaka Dec 16 '18 at 16:01
  • Side note: you should not rely on a default work factor / iteration count for PBKDF2 (even though 50K is OK-ish). But yeah, thirthoned on the salt and the idea of calling a `check` method rather than doing the compare manually (it's not seconded, so...). – Maarten Bodewes Dec 16 '18 at 16:16
  • Thank you for your comments! I think I have solved the problem. The generate_password_hash function is from werkzeug.security (check_password_hash, generate_password_hash). They include a check_password_hash function, which did the trick. I have copied my new code into the original question. – user10727666 Dec 16 '18 at 16:21
  • That's not a good idea. You could ask @kelalaka to post an answer as kelalaka already put it in a comment, or you could create your own. Questions are for questions, answers are for answers. – Maarten Bodewes Dec 16 '18 at 16:28
  • @kelalaka I just saw you mentioned the check_password_hash function as well. Thanks! Could you post this as the answer? – user10727666 Dec 16 '18 at 16:30

1 Answers1

1

As pointed in this answer some libraries generate the salt itself, you don't supply one. In this library, you can only supply the salt size. From the doc

werkzeug.security.generate_password_hash(password, method='pbkdf2:sha256', salt_length=8)

Hash a password with the given method and salt with a string of the given length. The format of the string returned includes the method that was used so that check_password_hash() can check the hash.

The format for the hashed string looks like this:

method$salt$hash

If you want to store the salt, parse it from the result. But this library has another function to check/verify the passwords;

werkzeug.security.check_password_hash(pwhash, password)

check a password against a given salted and hashed password value.

So;

In sign-up login, or to change passwords use;

generate_password_hash

To verify the passwords, use;

check_password_hash
kelalaka
  • 5,064
  • 5
  • 27
  • 44