How to retrieve passwords from a database - python

I am building a registration system for my web application wherein users provide a username and a password. This data is stored in a postgresql database. I am using bcrypt to generate a salted hash of the user entered password as follows
import bcrypt
hashed = bcrypt.hashpw(PasswordFromWebForm.encode('UTF-8'), bcrypt.gensalt())
This creates a salted password that looks something like this - b'$2b$12$GskbcRCMFHGuXumrNt3FLO'
I am storing this value in a postgresql database. Next, when a user tries to login to the system, I want to verify his/her credentials. To do this, I intend to do something along the lines of -
import psycopg2
conn = psycopg2.connect("dbname=test user=me")
cur = conn.cursor()
saltedpassword = cur.execute("SELECT saltedpassword FROM test WHERE loginid = %s", (LoginIDFromWebForm,))
if bcrypt.hashpw(PasswordFromWebForm.encode('UTF-8'), saltedpassword) == saltedpassword:
print("Success")
This does not work. It throws the following TypeError: Unicode-objects must be encoded before hashing error.
I suspect that this error is because the variable saltedpassword stores the value as a string like this "b'$2b$12$GskbcRCMFHGuXumrNt3FLO'" instead of just plain b'$2b$12$GskbcRCMFHGuXumrNt3FLO' (Notice the quotes enclosing the salted password in the former)
How do I get around this problem? What is the best way to store the salted hashed password in a database and how do I go about retrieving it when needed for verification? Apologies for the rather longish question - please help.

psycopg2 stores Unicode text, you must decode the salted password before inserting into the database:
cur.execute("INSERT INTO test VALUES (%s, %s)",
(LoginIDFromWebForm, saltedpassword.decode('ascii')))
This prevents the str() conversion being inserted instead (giving you "b'....'" in the database).
Next, when querying saltedpassword is not a string, because cursor.execute() does not return the results; it returns None instead.
You'll need to fetch the result row:
cur.execute("SELECT saltedpassword FROM test WHERE loginid = %s", (LoginIDFromWebForm,))
row = cur.fetchone()
if not row:
# No such login ID, handle accordingly
saltedpassword = row[0].encode('ascii')
Since rows contain Unicode text, you need to first encode to a bytestring before passing it to bcrypt. You also want to use the bcrypt.checkpw() function to check the password:
if bcrypt.checkpw(PasswordFromWebForm.encode('UTF-8'), saltedpassword):
print("Success")
bcrypt.checkpw() avoids timing attacks when comparing strings, something your code is vulnerable to.

Related

Why is check_password_hash always returning False?

The check_password_hash function always returns false.
I generate the hash like this
hashed_password = generate_password_hash('password',method='pbkdf2:sha1:2000', salt_length=15)
then saved in a database,
db.execute(
"INSERT INTO users (username, hash, Fullname, email) VALUES(?, ?,?,?)", username, hashed_password, Fullname, mail)
but I can't get it to validate using
# Query database for username
rows = db.execute("SELECT * FROM users WHERE username = ?",
request.form.get("username"))
# Ensure username exists and password is correct
if len(rows) != 1:
return apology("invalid username and/or password -0", 403)
if not check_password(rows[0]["hash"], request.form.get("password")):
I don't know what's wrong.
I did try creating this but same result
# check for password
def check_password(hash, password):
return check_password_hash(hash, password)
If I understand it correctly, and keep in mind I may be completely wrong, it seems to me that you are comparing a hashed password with what seems to be the raw user input.
My assumption is that in this line:
if not check_password(rows[0]["hash"], request.form.get("password")):
request.form.get("password") refers to the framework you are using and that's probably an unhashed password?
So (as others mentioned in the comments) it depends on what check_password_hash() does.
on another note, my experience with hashes is that they are often returned as bytes() / b'', which means you could be comparing 2 things that look the same, but technically are not.
UPDATE:
After a bit more digging it seems like you are using werkzeug.security, which clarifies a lot the functions in question. So I would proceed by adding a print(), for each value, right before you compare them. This way we can determine if there's something wrong with the data and approximately where it occurs.

MySQL-Python code not working, generating various errors each time i attempt to fix

I have a python script that interfaces with a mysql database i have. Each time i run it, it gives me a different error every time. I am becoming so confused as to what to do to fix it.
Basically, the code is meant for an user to input a search parameter for an account database, and the script searches a MySQL table and brings up data about the account.
As of now i get no errors, but it returns absolutely no search results.
I used to be able to make it search by an EXACT username, but i wanted it so you can search for a term within that username. Every attempt at the latter always results in some sort of error or i get no results back from MySQL.
import mysql.connector
users1 = mysql.connector.connect(
host="localhost",
user="python",
passwd="HaHaYou'reNotGettingMyPassword",
database="accounts"
)
cursor=users1.cursor()
usersearch = input("Please input the search term: ")
sql = ("SELECT user_id, username, date, status, description, gen FROM users1 WHERE username LIKE %s")
cursor.execute(sql, ('"' + usersearch + '"'))
result = cursor.fetchall()
for x in result:
print(x)
print("In Order: User ID, Username, Account Creation Date, bla bla bla")
EDIT: i figured out i think my SQL syntax is incorrect. i'll try an fix that and see if that was my only problem.
Try :
sql = ("SELECT user_id, username, date, status, description, gen FROM users1 WHERE username LIKE %s")
cursor.execute(sql, [usersearch])
If you get results when specifying a full username with the above, you probably need to add "wildcards" to your search term to "find within" existing strings. The wildcard "%" matches 0 or more characters.
E.g. change your string concatenation to:
cursor.execute(sql, ('"%' + usersearch + '%"'))
WARNING: This input style is wide open to SQL Injection Attack, as user input is directly used to create part of the SQL statement that is sent to the DB engine. Please see this answer for mitigation methods.

Python Sqlite3 Database table isn't being updated

I'm creating a change-password page for a website, which requests the new password and the current password. The old password is hashed and salted using the scrypt library then compared to the password stored in the sqlite3 database, and if these are a match, the new password is hashed and the database is updated. However I am having difficulty executing the update command, as it throws a sqlite3.OperationalError: unrecognised token: "\" error. The execute statement currently has the following code:
c.execute("UPDATE users SET password = \'{0}\' WHERE memberID = \'{1}\'".format(newPas, memID))
Initially we believed this error to have been caused by the use of ' in the string formatting due to the presence of ' within the new password itself, so this was run again as:
c.execute("UPDATE users SET password = \"{0}\" WHERE memberID = \"{1}\"".format(newPas, memID))
This successfully runs, but doesn't actually change anything in the database. We also attempted to create a query string and then execute the string.
query = "UPDATE users SET password = {0} WHERE memberID = {1}".format(newPas, memID)
c.execute(query)
This caused a sqlite3.OperationalError: near "'\xa1\x91\x9f\x88\xfb\x81\x12\xd4\xc2\xf9\xce\x91y\xf0/\xe1*#\x8aj\xc7\x1d\xd3\x91\x14\xcb\xa4\xabaP[\x02\x1d\x1b\xabr\xc7\xe4\xee\x19\x80c\x8e|\xc0S\xaaX\xc6\x04\xab\x08\x9b\x8e\xd7zB\xc6\x84[\xfb\xbc\x8d\xfc'": syntax error. I believe that this is caused by the presence of ' and " characters within the password, but I am unsure how to get around this issue as these are added by the hashing process and thus removing them would change the password.
The password I would like to add is:
b'\xa1\x91\x9f\x88\xfb\x81\x12\xd4\xc2\xf9\xce\x91y\xf0/\xe1*#\x8aj\xc7\x1d\xd3\x91\x14\xcb\xa4\xabaP[\x02\x1d\x1b\xabr\xc7\xe4\xee\x19\x80c\x8e|\xc0S\xaaX\xc6\x04\xab\x08\x9b\x8e\xd7zB\xc6\x84[\xfb\xbc\x8d\xfc'
I was wondering if anyone could share some insights into why it isn't liking the "\" character or why it isn't updating the database, and point me in the right direction to making it work. If you need more information or code snippets or just want to yell at me, please don't hesitate to! Thank you in advance :)
A couple of things with your code:
You should not use format to build your queries like this. This leaves you liable to SQL injection and, whilst you might sanitise your inputs in this case, it's a bad habit that will bite you.
All changes need to be committed to the database to actually take effect. This is why your second query did not throw an error but equally did not make any changes to the database.
The correct formatting of this query would be:
conn = sqlite3.connect('my_db.db')
c = conn.cursor()
query = "UPDATE users SET password = ? WHERE memberID = ?"
c.execute(query, (newPas, memID))
conn.commit() # To finalise the alteration
As a side note, the cursor expects a tuple in this case, so a common stumbling block comes when passing single values:
query = "UPDATE users SET password = ? WHERE memberID = 'abc'"
c.execute(query, (newPas)) # Throws "incorrect number of bindings" error
# Use this instead i.e. pass single value as a tuple
c.execute(query, (newPas,))
You could use format to create variable field names in a query, since placeholders are not allowed in this case:
fields = ['a', 'b', 'c']
query = "UPDATE users SET {} = ?".format(random.choice(fields))
in addition to using it to help you build big queries where it would be tedious to manually type all the placeholders, and difficult to ensure that you had the correct number if your code changed:
my_list = ['a', 'b',...., n]
placeholders = ', '.join(['?' for item in my_list])
query = "INSERT .... VALUES = ({})".format(placeholders)
You should use parametrized queries something like this:
c.execute("""UPDATE users SET password = ? WHERE memberID = ?;""", (newPas, memID))
It will allow to avoid nasty things like SQL-injections.

SQLite3 with selecting data (in python)

I am learning myself some programming with python and make use of SQlite3
I keep running into the same problem, and I can't figure out what goes wrong.
My table setup
def user_table():
data = lite.connect(database)
dat = data.cursor()
with data:
dat.executescript("""CREATE TABLE IF NOT EXISTS Users(Id INTEGER PRIMARY KEY AUTOINCREMENT,
'Username' UNIQUE,
'Password',
'Email',
'UserCharacters_Id' INTEGER
)""");
Now my code to select a Username (the username 123 exists and tables seem right (checked with SQLite studio)
database = 'test.db'
data = lite.connect(database)
dat = data.cursor()
with data:
dat.execute("SELECT * FROM Users WHERE 'Username'='123'")
user = dat.fetchone()
print user
I tried a lot of different ways, but it keeps returning None.
The python part seems to be working, just the select part of SQL goes wrong (checked with prints)
Please help me out
In SQL, single quotes are used for strings, while table/columns names are quoted with double quotes.
(SQLite supports single quotes for the latter for compatibility with MySQL in some places.)
Your query compares the string Username against the string 123, and this comparison fails for every record.
Use this:
dat.execute('SELECT * FROM Users WHERE "Username" = \'123\'')
But to prevent string formatting problems and SQL injection attacks, you should use parameters:
username = "123"
dat.execute('SELECT * FROM Users WHERE "Username" = ?', (username,))

Tornado: get_argument -- should I escape the input myself?

I'm running a Tornado web server for a single page application. The client is POSTing to the server and I'm using tornado.web.RequestHandler.get_argument() to get the input.
When testing, I can't seem to force an SQL injection bug. It looks like get_argument() somehow escapes the input. When doing a POST from a login-form (username + password) I've tried all sorts of tricks to force a simple SQL injection but to no avail.
EDIT2:
HAH! I managed to do an SQL injection at last :D I URL-escaped some of the input and I could see the injected SQL statement go all the way to the DB module.
The query I generate from the login-form does not get committed, as it's just supposed to be a SELECT statement - so I couldn't actually alter the database.
If the query never gets committed and the output of the whole query (including the injected) is hidden, what kind of damage can be done ?
For instance if the query is supposed to be, say SELECT * FROM Users WHERE UserID='USERNAME' AND Password='PASSWORD'; but the input for username has an INSERT injected, so USERNAME becomes USERNAME'; INSERT INTO Users (UserID, Password) VALUES ('hacker', 'hacked'); -- we end up with:
SELECT * FROM Users WHERE UserID='USERNAME'; INSERT INTO Users (UserID, Password) VALUES ('hacker', 'hacked'); --' AND Password='PASSWORD';
I am aware of the dangers of SQL injections in general, I'm just curious regarding this detail. I'm also aware I should hash and salt passwords, the code above is a simplification for the sake of the example.
Tornado only escapes the strings in the templates to avoid HTML issues. If you're just doing something like print self.get_argument('ihack') you'll get the raw string that is sent.
You should using MySQLdb with injection prevention:
cursor.execute("SELECT * FROM user_info WHERE email = %s", email)
Rather than:
cursor.execute("SELECT * FROM user_info WHERE email = %s" % email) # BAD!
This will protected your SQL just like the templates protect your HTML.

Categories

Resources