Click here to Skip to main content
15,609,235 members
Please Sign up or sign in to vote.
1.00/5 (1 vote)
See more:
I am trying to verify user login but it's not working as expected. The password stored in the database is encoded and hashed. There is a login class which has a function named sqllog, this is where the code for logging in is. There is another class named registerTB, this class has a function named hash_password. Now to enable my login verification, I need to use one of the variables of the hash_password function, this variable is named SALT. I am having problem using it. I called the registerTB class and its function "hash_password" in the sqllog function of the Login class but when I run the code and try to log in, it runs the hash_password function and then reject the inputted password as incorrect even though it is correct.

Here is my code:
Python
def SQLLog(self):                #this calls the hashpassword method of class registerTB because I need to use the SALT variable there.
registerTB().hash_password()
msgBox = QMessageBox()
msgBox.setIcon(QMessageBox.Information)
msgBox.setGeometry(100, 100, 100, 100)
try:
    username = self.user.text()
    passwordd = self.pw.text()
    hashpass = hashlib.pbkdf2_hmac("sha256", passwordd.encode(), salt, 10000)
    strhash = str(hashpass)
    print(strhash)

    conn = pyodbc.connect('Driver={SQL Server}; Server=mine1;database=logistics; Trusted_Connection=yes;')
    result = conn.execute("SELECT * FROM employees WHERE userID = ? AND pw = ?", (username, strhash))
    print("connected to database")
    if (len(result.fetchall()) > 0):
        print("user found")
        msgBox.setText("LOGIN SUCCESSFUL")
        msgBox.setWindowTitle("MESSAGE FROM DB")
        msgBox.exec_()
        self.toggle_logistics()

    else:
        print("user not found")


Here is the code for the hashpassword function:

def hash_password(self):
try:
global salt
password = self.pwTx.text()
pwcon = self.pwCtx.text()
salt = os.urandom(16)
password_hash = hashlib.pbkdf2_hmac("sha256", password.encode(), salt, 10000)
pwcon_hash = hashlib.pbkdf2_hmac("sha256", pwcon.encode(), salt, 10000)

self.strpw = str(password_hash)
self.strpcc = str(pwcon_hash)

I want users to be able to login and their correct passwords match with what are stored in the database table.

What I have tried:

I tried calling the class with the SALT attributes but it seems I was not doing it right.
I made the SALT attribute global also so it can be called and used anywhere but it's now working. It's insisting I must define it in the new class
Posted
Updated 8-Sep-21 5:20am
v2
Comments
CEO CEO 8-Sep-21 15:41pm    
Hello Mr. Richard, I noticed something again. Am I to also create a column for the plain password in my database?

I saw in your sql statement where you wrote SELECT FROM pw, SALT,

Please is that pw for the plain password or the hashed password generated earlier?

1 solution

You can't do it like that!

You need to have a separate random salt for each password. Which means that you need to store the salt alongside the password.

As a result, you cannot filter on the pw column in your database. Instead, you need to load the salt and the hashed password for the supplied username, calculate the hash using the entered password and the stored salt, and then compare it to the hashed password from the database.

Secure Password Authentication Explained Simply[^]
Salted Password Hashing - Doing it Right[^]

Quote:
No, you're not to produce a separate salt for the password, confirmation and login verification. That would make the passwords different.
That's the whole point! You have a different salt value for each stored password. When you verify the entered password, you combine it with the salt value stored against that user's record to produce the hash, and then compare that hash to the stored hash.

The whole point of a salt is to make it much harder to find the original password, since an attacker can no longer rely on a "rainbow table" of precomputed hashes. It also makes it impossible for the attacker to see which accounts have the same password, since the random salt ensures that the stored hash will be different even if the plain-text password is the same.

EDIT 2:
I don't "do" Python, but the basic approach is as follows:
Python
username = self.user.text()
password = self.pw.text()

conn = pyodbc.connect('Driver={SQL Server}; Server=mine1;database=logistics; Trusted_Connection=yes;')
print("connected to database")

result = conn.execute("SELECT pw, salt FROM employees WHERE userID = ?", username)
row = result.fetchone()
if row:
    print("user found")
    
    storedhash = row[0]
    salt = row[1]
    
    hashpass = str(hashlib.pbkdf2_hmac("sha256", password.encode(), salt, 10000))
    
    if hashpass == storedhash:
        msgBox.setText("LOGIN SUCCESSFUL")
        msgBox.setWindowTitle("MESSAGE FROM DB")
        msgBox.exec_()
        self.toggle_logistics()

    else:
        print("Invalid credentials")

else:
    print("Invalid credentials")

Obviously, you'll need a column in your database to store the salt value, and you'll need to update your user registration code to store the generated salt value in that column.

And avoid using global variables.
 
Share this answer
 
v3
Comments
Richard Deeming 8-Sep-21 12:05pm    
Look at my updated solution, from the "Edit 2" line onwards.
Richard Deeming 8-Sep-21 12:37pm    
You're really going out of your way not to listen, aren't you? 🤦‍♂️

You need a DIFFERENT salt value for each user. That value needs to be stored in the database. When you verify the password, you read the stored salt, combine it with the entered password, and compute the hash. You then compare that to the stored hash.

DO NOT use the same salt for multiple users. DO NOT store the salt in a global variable.

If you don't want to take my word for it, read the articles I linked to. Or read any reputable source. Or look at how any reputable library implements this.
CEO CEO 8-Sep-21 15:29pm    
Hello Mr. Richard,

I'm home now. I have taken my time to go through your comments and you made lot of sense. I am sorry for earlier. I admire and appreciate your patience with me.

Yes, I have created columns for SALT and pwKey, then SALT + pwKey becomes passwordStorage.

I have removed the global characteristics from the attributes SALT and pwkey.

Since the login function is in a different class from the hashpassword function, I was wondering how I would use the attributes passwordStorage and SALT which are attributes in the hashpassword function (method) in the registerTB class, that was the reason I initially globalized it but from your explanation so far, I have understood it.

The sql statement was where I was really disturbed on how it could do the verification but you have explained it better in your code presentation.

I will try everything you have suggested and give you a feedback. Thanks a lot and do accept my apology.
CEO CEO 9-Sep-21 4:34am    
Hello Mr. Richard, your code sample example really went a long way in guiding me. I have resolved everything and the program works fine and as expected. Thank you so much. Indeed a teacher affects eternity
Richard Deeming 9-Sep-21 6:06am    
Excellent! Glad you got it sorted at last. :)

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900