Hashing gives different result - python

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?

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())

Related

User authentication not working in flask and sqlite

What I am trying to do is to get the email id and compare against the SQLite table.
If email exists in the table then I update the table with the emailid and random generated password and mail them.
If email does not exists in the table then I use insert query to enter the email as well as random generated password into the table.
After the insert or the update query is fired I mail them the generated password using Flask-mail
However I am unable to execute it
def sqliteconfig():
try:
conn = sqlite3.connect('auth.db',check_same_thread=False)
cur = conn.cursor()
conn.execute('CREATE TABLE IF NOT EXISTS auth (AID INTEGER PRIMARY KEY AUTOINCREMENT, emailid TEXT UNIQUE, otp TEXT, created_at TEXT DEFAULT CURRENT_TIMESTAMP)')
cur.close()
except Exception as e:
print(e)
return 'DatabaseDown'
# return 'DatabaseDown'
return conn
#bp.route('/')
def index_redirect():
return redirect(url_for('devcon.login'))
#bp.route('/login',methods=['GET','POST'])
def login():
conn = sqliteconfig()
cur = conn.cursor()
if request.method == 'POST':
emailid = request.form['emailid']
if emailid != "":
s = "abcdefghijklmnopqrstuvwxyz01234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ"
passlen = 8
password = "".join(random.sample(s,passlen ))
conn.execute('select count(*) from auth where emailid=(?)',[emailid])
rows = cur.fetchall();
if len(rows) == 0:
conn.execute('insert into auth(email,otp) values(?,?)',[emailid,password])
conn.commit()
elif len(rows)==1:
conn.execute('update auth SET otp=(?) where emailid=(?)',[emailid,password])
conn.commit()
return str(rows)
return render_template("login/login.html")
The Particular problem I am facing right know is SELECT COUNT query returns nothing and INSERT query throws constraint violation error of unique emailid.
I am looking forward if there is any better way to do this
For the first error where SELECT COUNT returns nothing, in Sqlite3 select * is used instead of select count(*). Therefore your code should be:
rows = conn.execute('SELECT * FROM auth WHERE emailid = ?',(emailid,)).fetchall()
For the second insertion error, you may already have an equivalent emailid value stored into auth. That is the only reason why you would have a constraint violation of an unique emailid.
Another (potential) error is that you set otp to emailid and password to emailid, while the order should be reversed:
conn.execute('update auth SET otp=(?) where emailid=(?)',[emailid,password])
Instead, do this:
conn.execute('UPDATE auth SET otp = ? WHERE emailid = ?',(password, emailid))
Final code:
def sqliteconfig():
try:
conn = sqlite3.connect('auth.db',check_same_thread=False)
cur = conn.cursor()
conn.execute('CREATE TABLE IF NOT EXISTS auth (AID INTEGER PRIMARY KEY AUTOINCREMENT, emailid TEXT UNIQUE, otp TEXT, created_at TEXT DEFAULT CURRENT_TIMESTAMP)')
cur.close()
except Exception as e:
print(e)
return 'DatabaseDown'
# return 'DatabaseDown'
return conn
#bp.route('/')
def index_redirect():
return redirect(url_for('devcon.login'))
#bp.route('/login',methods=['GET','POST'])
def login():
conn = sqliteconfig()
cur = conn.cursor()
if request.method == 'POST':
emailid = request.form['emailid']
if emailid != "":
s = "abcdefghijklmnopqrstuvwxyz01234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ"
passlen = 8
password = "".join(random.sample(s,passlen ))
rows = conn.execute('SELECT * FROM auth WHERE emailid = ?',(emailid,)).fetchall()
if len(rows) == 0:
conn.execute('INSERT into auth (email, otp) VALUES (?, ?)',(emailid, password))
conn.commit()
elif len(rows)==1:
conn.execute('UPDATE auth SET otp = ? WHERE emailid = ?',(emailid, password))
conn.commit()
return str(rows)
return render_template("login/login.html")

Why does Bcrypt keep failing with MySQL, and how do I retrieve the hash?

I am trying to take a password from React Native into Flask via URL and then insert it into MySQL. I then retrieve it from DB and test it against the password. I've tried every combination of making the hash and password strings and encoded utf8 but I only ever get false, please let me know what's wrong.
my get password method that hashes and inserts into DB
#app.route('/signup', methods=['GET'])
def signup():
password = str(request.args.get('password')).encode('utf8')
username = str(request.args.get('username')).encode('utf8')
c.execute("""INSERT INTO profiles
(username, password
)
VALUES
(%s, %s)""", ## gender true==male && false==female
(username, hashed
)
)
con.commit()
I get a row in mysql looking like this (varchar256)
+--------------------------------------------------------------+
| password |
+--------------------------------------------------------------+
| $2b$12$oD607B1ej5qXM/mFcPVdueX8R5zeWNfL39d2oNPuoM3KdUfP8McvO |
+--------------------------------------------------------------+
my retrieve password method
#app.route('/check_hash', methods=['GET'])
def check_hash():
try:
username = request.args.get('username')
password = str(request.args.get('password')).encode("utf8")
c.execute('select password from profiles where userID = ' + '"' + str(username) +'"')
hashed = (c.fetchone()[0]).encode('utf8')
if bcrypt.hashpw(password, hashed) == (hashed):
return json.dumps('success')
else:
return json.dumps('no')
except Exception as e:
return str(e)
Failed every time with false when expected is true

SQLITE3 FROM table Select column WHERE Boolean statement

I have tried 3 different variations of sqlite3 statement to SELECT a data:
cursor.execute('SELECT * FROM users WHERE username = ?', (username,))
cursor.execute('''SELECT * FROM users WHERE username = ?;''', (username,))
cursor.execute('SELECT * FROM users WHERE username = "monkey1" ')
References for these statements are from 1 2. However, none of them worked. I suspect that I am doing something really silly but can't seem to figure this out.
I want to be able to print out the data of username "monkey". Appreciate any help to point out my silly mistake.
import sqlite3
import datetime
def get_user(connection, rows='all', username=None ):
"""Function to obtain data."""
#create cursor object from sqlite connection object
cursor = connection.cursor()
if rows == 'all':
print("\nrows == 'all'")
cursor.execute("SELECT * FROM users")
data = cursor.fetchall()
for row in data:
print(row)
if rows == 'one':
print("\nrows == 'one'")
cursor.execute('SELECT * FROM users WHERE username = ?', (username,))
#cursor.execute('''SELECT * FROM users WHERE username = ?;''', (username,))
#cursor.execute('SELECT * FROM users WHERE username = "monkey1" ')
data = cursor.fetchone()
print('data = ',data)
cursor.close()
return data
def main():
database = ":memory:"
table = """ CREATE TABLE IF NOT EXISTS users (
created_on TEXT NOT NULL UNIQUE,
username TEXT NOT NULL UNIQUE,
email TEXT NOT NULL UNIQUE
); """
created_on = datetime.datetime.now()
username = 'monkey'
email = 'monkey#gmail'
created_on1 = datetime.datetime.now()
username1 = 'monkey1'
email1 = 'monkey1#gmail'
# create a database connection & cursor
conn = sqlite3.connect(database)
cursor = conn.cursor()
# Insert data
if conn is not None:
# create user table
cursor.execute(table)
cursor.execute('INSERT INTO users VALUES(?,?,?)',(
created_on, email, username))
cursor.execute('INSERT INTO users VALUES(?,?,?)',(
created_on1, email1, username1))
conn.commit()
cursor.close()
else:
print("Error! cannot create the database connection.")
# Select data
alldata = get_user(conn, rows='all')
userdata = get_user(conn, rows='one', username=username )
print('\nalldata = ', alldata)
print('\nuserdata = ', userdata)
conn.close()
main()
Your table definition has the fields in order created_on, username, email but you inserted your data as created_on, email, username. Therefore the username of the first row was 'monkey#gmail'.
A good way to avoid this kind of mistake is to specify the columns in the INSERT statement rather than relying on getting the order of the original table definition correct:
INSERT INTO users (created_on, email, username) VALUES (?,?,?)

How to raise error message on duplicate entry while inserting data into sqlite3 with python?

Guys I'm using sqlite3 with python tkinter as front end. The database is a simple one with two fields, username and password. I want to make a registration sign up page. where data given in the two fields to be stored in a sqlite3 database. Data is inserting properly. But I want to display a messagebox when the username provided already exist in the database. I tried the below code.
MY CODE:
def signup():
userID = username.get()
passwd = password.get()
conn = sqlite3.connect('test.db')
c = conn.cursor()
result = c.execute("SELECT * FROM userstable")
for i in result:
if i[0] == userID:
messagebox.showerror("DUPLICATE", "USER ALREADY EXISTS!")
else:
conn = sqlite3.connect('test.db')
c = conn.cursor()
c.execute("INSERT INTO userstable VALUES (?, ?)", (userID, passwd))
conn.commit()
c.close()
conn.close()
username.delete(0,END)
password.delete(0,END)
username.focus()
messagebox.showinfo("SUCCESS", "USER CREATED SUCCESSFULLY")
This works but still the duplicate data is being stored after the error message. My requirement is to throw the error and stop executing if the username is already available. If the username is not available already it should insert the data.
Where am I going wrong? could some one explain me by pointing out or is there any other way I can achieve this? It seems I need to modify something to my function. Please guide me.
EDIT 1
If i try to use three conditions the break is not working.
MY CODE
def data_entry():
conn = sqlite3.connect('test.db')
c = conn.cursor()
c.execute('CREATE TABLE IF NOT EXISTS userstable(username TEXT, password TEXT)')
username = uname.get()
password = passwd.get()
result = c.execute("SELECT * FROM userstable")
if username != '' or password != '':
for i in result:
if i[0] == username:
tkinter.messagebox.showerror("DUPLICATE", "USER ALREADY EXISTS!")
break
else:
c.execute('INSERT INTO userstable (username, password) VALUES(?, ?)',(username,password))
conn.commit()
c.close()
conn.close()
another_clear()
tkinter.messagebox.showinfo("Success", "User Created Successfully,\nPlease restart application.")
else:
tkinter.messagebox.showerror("ERROR", "Fill both fields!")
A better way to approach this would be to create a unique constraint (index, in sqlite) on the table to prevent the insertion of a duplicate username. That way, you can try/except the insert statement instead of looping through a list of all users to see if it already exists (that's not scalable). This would also prevent you from having to "select *", which is generally bad practice (try to be explicit).
https://sqlite.org/lang_createtable.html
So, you could add the constraint either as a unique index, or as a primary key. If you only have 2 columns in this table, or if you have more than 2 but no additional IDs, your username can be your primary key. If you are introducing a system ID for your users, I'd use that as your primary key and username as a unique index. Either way, you'll need to alter your table to add the constraint.
CREATE UNIQUE INDEX username_uidx ON userstable (username);
Again, because you're not explicitly letting us know the column names, you'll have to fill that in.
After that:
try:
conn = sqlite3.connect('test.db')
c = conn.cursor()
c.execute("INSERT INTO userstable VALUES (?, ?)", (userID, passwd))
conn.commit()
except: # I'm not sure the exact error that's raised by SQLite
messagebox.showerror("DUPLICATE", "USER ALREADY EXISTS!")
finally:
c.close()
conn.close()
I typically wrap my cursor and connections in a finally so that they close even if there's an exception. That's not 100% of what you need, but it should get you there in one step with better DB design to enforce the uniqueness on a user.
I advise against using a loop with an else statement, it can be confusing.
See this post why-does-python-use-else-after-for-and-while-loops for more info.
If you want to use for - else you can add a break, so else will not be executed :
for i in result:
if i[0] == userID:
messagebox.showerror("DUPLICATE", "USER ALREADY EXISTS!")
break
else:
...
Or you can use a true / false flag :
user_exists = False
for i in result:
if i[0] == userID:
messagebox.showerror("DUPLICATE", "USER ALREADY EXISTS!")
user_exists = True
if not user_exists :
...

How do I query SQlite using python and then compare the query result?

The aim of this code is to validate a password that is entered using the passwords and usernames stored in a database. The table contains username (nick) and password, passwords are encrypted using db.crypt.
My code at the moment consists of:
def check_login(db, username, password):
"""returns True if password matches stored"""
cursor = db.cursor()
pass1 = db.crypt(password)
cursor.execute("SELECT password FROM users WHERE nick=?", (username,))
passcheck = cursor.fetchone()
if passcheck == pass1:
return True
else:
return False
But i keep getting an assertion error when running a unit test:
line 29, in test_check_login
self.assertTrue(users.check_login(self.db, username, password), "Password check failed for username%s" % username)
AssertionError: False is not true : Password check failed for nick jake
When I use print to show what passcheck is retrieving it prints the correct encrypted pass word but inside of ('') tags (as a tuple, I believe). But when I print cursor.fetchone() it says None. I'm unsure what is going wrong here, I encrypted the password being sent into the function so it can be correctly matched to that of the password stored in the DB. I assume passcheck would retrieve the password that corresponds to the user nick, that is passed through the function.
Any and all help is much appreciated.
cursor.fetchone()
fetches a row as a tuple:
passcheck == ("hashed password", )
You must compare agains passcheck[0] or unpack the tuple:
passcheck, = cursor.fetchone()
Edit: Let the database do the comparing:
cursor.execute("SELECT * FROM users WHERE username= ? and password= ?",
(username, pass1))
found = cursor.fetchone()
if found:
# user exists and password matches
else:
# user does not exist or password does not match

Categories

Resources