Search code examples
pythonmysqlhashsalt-cryptographyhashlib

Hashing gives different result


I am using Python and MySql to handle user authentication. I have added the users thru python with the exact same method, but when I try to do the "login"/authentication it does not match.

This is my code for authentication:

# Collecting data from users. 
username=data['username']
password=data['password']

cur=mysql.connection.cursor()

# Checks if username exists. 
check_username = cur.execute("SELECT * FROM `users` WHERE `username`=%s",[username])

if check_username > 0:

     # Retrieves the correct salt of the user.
     cur.execute("SELECT `salt` FROM `users` WHERE `username`=%s",[username])
     get_salt = cur.fetchone()

     # Generates the hashed password of the user. 
     hashed_password=(hashlib.sha256((str(password[0:2])+str(get_salt[0])+str(password[2:])).encode('utf-8')).hexdigest()).lower()

     # Retrieves the hashed password of the user from the database. 
     cur.execute("SELECT `password` FROM `users` WHERE `username`=%s",[username])
     get_password = cur.fetchone()

     # Checks if identical.
     if get_password[0] == hashed_password:

          return jsonify("Authentication successful!"),201

     else: return jsonify("Authentication failed!"),401

else: return 'Incorrect username. Please try again.'

The hashed_password does not return the same hashed password that is stored in the database.

And this is the code I used to insert the users.

username=data['username']
password=data['password']
salt=data['salt']

cur=mysql.connection.cursor()

# Generates the hashed password of the user. 
hashed_password=(hashlib.sha256((str(password[0:2])+str(salt[0])+str(password[2:])).encode('utf-8')).hexdigest()).lower() 

# Adds user to database.
add_user = cur.execute(" INSERT INTO `users` (`username`, `password`, `salt`) VALUES (%s, %s, %s);",[username,hashed_password, salt])

Does anyone see what is causing this?


Solution

  • Looking at the insertion code, you seem to treat salt like the get_salt tuple, get first item, not knowing what it is originally, that might be the source of your issues as I would not expect the first salt you get to be in a tuple.

    Here is a version that works, it's using SQLite rather than MySQL, but the changes are minimal besides formatting.

    Also, I recommend you use hashlib.hash.update() rather than your large and complicated one step hashing. And by default hexdigest is lowercase, so no need to .lower() it.

    # db setup
    import hashlib
    import sqlite3
    
    con = sqlite3.connect(":memory:")
    
    cur = con.cursor()
    
    cur.execute("CREATE TABLE users (username TEXT, salt TEXT, password TEXT)")
    
    in_username = "ljmc"
    in_salt = "verysecuresalt"
    in_password = "p4assW0rD1!"
    
    h = hashlib.sha256()
    h.update(str(in_password[:2]).encode("utf-8"))
    h.update(str(in_salt).encode("utf-8"))  # here salt[0] is just b"v" not the whole salt
    h.update(str(in_password[2:]).encode("utf-8"))
    in_hashed_password = h.hexdigest()
    
    cur.execute(
        "INSERT INTO users VALUES (?, ?, ?)",
        (in_username, in_salt, in_hashed_password), # then store whole salt
    )
    
    con.commit()
    
    # user check
    username = in_username
    password = good_password
    
    # Checks if username exists.
    check_username = cur.execute(
        "SELECT COUNT(*) FROM users WHERE username = ?", [username]
    ).fetchone()
    
    if check_username[0] > 0:
    
        # Retrieves the correct salt of the user.
        cur.execute("SELECT salt FROM users WHERE username=?", [username])
        get_salt = cur.fetchone() # get the whole salt
    
        # Generates the hashed password of the user.
        hashed_password = (
            hashlib.sha256(
                (str(password[0:2]) + str(get_salt[0]) + str(password[2:])).encode(
                    "utf-8"
                )
            ).hexdigest()
        ).lower() # use the whole salt
    
        # Retrieves the hashed password of the user from the database.
        cur.execute("SELECT password FROM users WHERE username=?", [username])
        get_password = cur.fetchone()
    
        # Checks if identical.
        if get_password[0] == hashed_password:
            print("Authentication successful!")
        else:
            print("Authentication failed!")
    

    As pointed out by other comments, it would also be a lot better to fetch all the user data in one query and then apply the logic, so the check block would look like this.

    cur.execute(
        "SELECT username, salt, password FROM users WHERE username = ?",
        (username,),
    )
    
    if user := cur.fetchone():
        out_username, out_salt, out_password = user
    
        h = hashlib.sha256()
        h.update(str(password[:2]).encode("utf-8"))
        h.update(str(out_salt).encode("utf-8"))
        h.update(str(password[2:]).encode("utf-8"))
        print(f"is the password {password} ?", out_password == h.hexdigest())