I am making a username password program using sqlite and I want to check if a username is in the database (I've already done this) then I want to find the row id of said username. This will be a username that the user has input. I know how to find the rowid of a word in the database e.g 'Word'. How would I make is so I could replace the word with a variable?
def sign_in():
usernameask = input("What is your username?")
passwordask = input("What is your password?")
c.execute("SELECT username FROM stuffToPlot")
names = {name[0] for name in c.fetchall()}
if usernameask in names:
print("Yes")
c.execute("SELECT password FROM stuffToPlot")
passs = {name[0] for name in c.fetchall()}
if passwordask in passs:
print("yes,pass")
t = c.execute("SELECT rowid, FROM stuffToPlot WHERE username = 'usernameask' ")
rowid = t.fetchall()
for r in rowid:
print(r)
else:
print("No,pass"
I am looking at where it says t = c.execute("SELECT rowid, FROM stuffToPlot WHERE username = 'usernameask' ")
and want to replace the 'usernameask' which is currently looking for it as a word in the database to a variable. How would I do this?
There is no error, it just finds the position of the word "usernameask" which isn't in the database.
You want to use a parameterised query. You put placeholders in your query where your data has to go, and leave it to the database driver to put your data and the query together.
Parameterised queries let you avoid a common security pitfall, the SQL injection attack, where an attacker can 'augment' your database query by putting in more commands than you originally anticipated. Query parameters always make sure your data is only ever handled as data, not as commands.
A parameterised query us usually also faster, as it lets the database avoid having to parse your query every time if you use it more than once, and it can also reuse query plans.
The sqlite3 database library uses ? for positional parameters; put a ? where ever you need to use data from your code, and put the parameter values in a sequence (like a tuple or a list) in the second argument to cursor.execute():
t = c.execute("SELECT rowid, FROM stuffToPlot WHERE username = ?", (usernameask,))
Note that (usernameask,) is a tuple with one element. You could also use [usernameask].
This executes your SELECT query using the string value that usernameask references in the WHERE username = filter. The driver takes care of quoting your value properly.
You could also use named parameters, these take the form of :parametername, (where you can pick your own names), and then you use a dictionary for the second argument to cursor.execute(), mapping names to values:
t = c.execute(
"SELECT rowid, FROM stuffToPlot WHERE username = :username",
{'username': usernameask})
Here the placeholder is named username, and the dictionary maps that to the usernameask value.
Related
I have a sql query I'm executing that I'm passing variables into. In the current context I'm passing the parameter values in as f strings, but this query is vulnerable to sql injection. I know there is a method to use a stored procedure and restrict permissions on the user executing the query. But is there a way to avoid having to go the stored procedure route and perhaps modify this function to be secure against SQL Injection?
I have the below query created to execute within a python app.
def sql_gen(tv, kv, join_kv, col_inst, val_inst, val_upd):
sqlstmt = f"""
IF NOT EXISTS (
SELECT *
FROM {tv}
WHERE {kv} = {join_kv}
)
INSERT {tv} (
{col_inst}
)
VALUES (
{val_inst}
)
ELSE
UPDATE {tv}
SET {val_upd}
WHERE {kv} = {join_kv};
"""
engine = create_engine(f"mssql+pymssql://{username}:{password}#{server}/{database}")
connection = engine.raw_connection()
cursor = connection.cursor()
cursor.execute(sqlstmt)
connection.commit()
cursor.close()
Fortunately, most database connectors have query parameters in which you pass the variable instead of giving in the string inside the query yourself for the risks you mentioned.
You can read more on this here: https://realpython.com/prevent-python-sql-injection/#understanding-python-sql-injection
Example:
# Vulnerable
cursor.execute("SELECT admin FROM users WHERE username = '" + username + '");
# Safe
cursor.execute("SELECT admin FROM users WHERE username = %s'", (username, ));
As Amanzer mentions correctly in his reply Python has mechanisms to pass parameters safely.
However, there are other elements in your query (table names and column names) that are not supported as parameters (bind variables) because JDBC does not support those.
If these are from an untrusted source (or may be in the future) you should be sure you validate these elements. This is a good coding practice to do even if you are sure.
There are some options to do this safely:
You should limit your tables and columns based on positive validation - make sure that the only values allowed are the ones that are authorized
If that's not possible (because these are user created?):
You should make sure tables or column names limit the
names to use a "safe" set of characters (alphanumeric & dashes,
underscores...)
You should enquote the table names / column names -
adding double quotes around the objects. If you do this, you need to
be careful to validate there are no quotes in the name, and error out
or escape the quotes. You also need to be aware that adding quotes
will make the name case sensitive.
I'm trying to fetch one row of a MySQL table based on a user input. My code is:
"""Get entry information"""
reinforcement_name = reinforcement_input.get()
"""Fetch reinforcement data"""
mycursor = mydb.cursor()
sql_select_query = "select '*' from reinforcement where name = '%s'"
mycursor.execute(sql_select_query, reinforcement_name)
reinforcement_data = mycursor.fetchone()
print(reinforcement_data)
I'm expecting to get a list of column entries for that specific user input. However, I'm getting None printed out rather than a list.
How can I have my reinforcement_data presented as the list I'm expecting?
The fetchone call is OK. The problem is with your query - when you use a parameterized query, you should not use quotes around the bind variables - these aren't simple string replacements, but bind variables the database driver handles. Here, you're querying for a row with the literal name of %s, which probably doesn't exist. The parameters need to pass to execute as an iterable.
On a related note, you should also remove the quotes around the * in the select list, otherwise, you'll get the asterisk literal, not all the columns.
sql_select_query = "select * from reinforcement where name = %s"
# No quotes --------------^-^-------------------------------^--^
mycursor.execute(sql_select_query, (reinforcement_name,))
# Params passed as a tuple --------^
Having a little tricky issue with python and mysql. To keep it simple, the following code returns whatever is in the variable 'field', which is a string. Such as 'username' or 'password'.
options = [field, userID]
entries = cursor.execute('select (?) from users where id=(?)', options).fetchall()
print(entries);
This code works correctly if I remove the first (?) and just use the actually name (like 'username') instead. Can anyone provide some input?
Your query is actually formed as:
select "field" from users where id="value"
which returns you a string "field" instead of the actual table field value.
You cannot parameterize column and table names (docs):
Parameter placeholders can only be used to insert column values. They
can not be used for other parts of SQL, such as table names,
statements, etc.
Use string formatting for that part:
options = [userID]
query = 'select {field} from users where id=(?)'.format(field=field)
cursor.execute(query, options).fetchall()
Related threads with some more explanations:
pysqlite: Placeholder substitution for column or table names?
Python MySQLdb: Query parameters as a named dictionary
I'm making a program that is a user interface for quizes set by teachers in a primary school. I am trying this query which is using data typed in by the user on the previous page. it is looking for people in the database who match the username and quiz number concerned. this is so the teacher can see how well pupils are doing on certain quizes.
Here is my code.
dbDatabase = sqlite3.connect('c:\\xampp\\cgi-bin\\MakingATable.db')
cuDatabase = dbDatabase.cursor()
Fieldstorage = cgi.FieldStorage() #what you typed in on the webpage
Quizno = Fieldstorage.getvalue("quizno")
UserID = Fieldstorage.getvalue("username")
#print (Quizno)
#print (UserID)
cuDatabase.execute ("""
SELECT Result
FROM resultstable
WHERE QuizID = '""" + str(Quizno) + """
AND UserID = '""" + UserID + "'")
for (row) in cuDatabase:
print (row)
dbDatabase.commit()
cuDatabase.close()
Here is the error message i am getting when i run my webpage:
40 FROM resultstable
41 WHERE QuizID = '""" + str(Quizno) + """
=> 42 AND UserID = '""" + UserID + "'")
43
44 for (row) in cuDatabase:
AND undefined, UserID = 'HuPa1'
OperationalError: near "HuPa1": syntax error
args = ('near "HuPa1": syntax error',)
with_traceback = <built-in method with_traceback of OperationalError object>
Also should I use an OR instead of AND so that if the user hasn't done that quiz it will display any quiz the user does. or so that if lots of people have done one Quiz then the teacher will see everyone who, for example, have done quiz 1?
You should use SQL parameters:
cuDatabase.execute ("""
SELECT Result
FROM resultstable
WHERE QuizID = ?
AND UserID = ?""", (Quizno, UserID))
The ? placeholders will be replaced by your values, automatically quoted to prevent SQL injection attacks (and operational errors).
Quoting from the sqlite3 module documentation:
Usually your SQL operations will need to use values from Python variables. You shouldn’t assemble your query using Python’s string operations because doing so is insecure; it makes your program vulnerable to an SQL injection attack (see http://xkcd.com/327/ for humorous example of what can go wrong).
Instead, use the DB-API’s parameter substitution. Put ? as a placeholder wherever you want to use a value, and then provide a tuple of values as the second argument to the cursor’s execute() method. (Other database modules may use a different placeholder, such as %s or :1.)
Use a separate query to ask the database for other quizzes if this query doesn't return a result; if you use OR instead of AND otherwise, you will get both quizz results that other users have done, and anything this user has completed.
The best solution is to use prepared statement:
cuDatabase.execute("SELECT Result FROM resultstable WHERE QuizID=? AND UserID=?", Quizno, UserID)
In prepared statement mode all variables are replaced by question marks in query text and are parameters to execute(). But not all databases or db drivers support it. Sometimes instead of question mark you will have to use %s (one of PostgreSQL drivers works this way).
Worse, but working solution is to use Python % operator, but with this solution you will have to use your own quote() function that escapes dangerous characters that may appear in data (prevents from SQL injection):
cuDatabase.execute("SELECT Result FROM resultstable WHERE QuizID='%s' AND UserID='%s'" % (quote(Quizno), quote(UserID)))
I have an SQL database that I access using Python and pgdb. I plan on opening access to a group of users, so I would like to incorporate a login function that takes in a username and password that is checked against the database's information on the user. I don't use Python that much, so this has taken me a considerable amount of time with little results. I've found helpful threads on this using PHP but not in Python unfortunately, so I decided to start this thread.
Below is my code. Basically, I would like a login function that returns true if the user exist with a given password. The code below is unresponsive.
Am I setting up the .execute incorrectly with the parameter references? Or is the if statement wrong?
def Login(username,password):
cursor.execute('select %s from db') % username
dbuser = cursor.fetchone()
if dbuser == username:
cursor.execute('select %s from db') % password
dbpass = cursor.fetchone()
if dbpass == password:
return "True"
else:
return "Password is incorrect."
else:
return "Username is incorrect."
Any help is greatly appreciated! Sorry for the newb question...
Three things...
First, the SQL you're using doesn't make any sense. Basic syntax is select columns from table where stuff is true. You are trying select <a username> from table where the username is supposed to be a column name which doesn't make any sense. I presume you were trying to something more like select username from db where password=%s
Second, telling the user that the username doesn't exist allows someone to build a list of all the valid user names through trial and error. Instead, tell them generically that the credentials don't match and don't tell them which part is broken.
Third, you are embedding your user input (username and passwords are input) directly into your SQL. Please do some reading on SQL Injection and use parameterized queries instead to prevent your users from doing very very bad things to your site.
Edit
One final consideration. This all implies that you're storing passwords in plain text in your table. Do some research on password hashing as well.
You are attempting to select a COLUMN from a database, that is equal to the username variable passed to the Login function.
Your syntax is utterly incorrect.
Read this PostgreSQL link on SQL Select for the correct syntax.
Essentially, what you need to do is select the full column (by column name) from your database, and add in a WHERE clause that uses or matches the username variable
cursor.execute('SELECT username,password FROM db WHERE username=%s') % (username)
Disclaimer: The above is insecure code for SQL injection prevention. It is only shown to help you understand the correct syntax to use, in place of what the current question has asked.