Avoiding SQL injection in Flask SQL Alchemy - python

I want to take the username from a login field after submission to query my SQL Alchemy database. In the past I used things like the following:
rows = db.execute("SELECT * FROM users WHERE username = :username", username=request.form.get("username"))
However this was in CS50 and I am now using SQL Alchemy and want to know how to use a similar ":username" statement. So far this is all I have:
userinfo = User.query.filter_by(username= :username).first(), username=request.form.get("username")
This does not work. I am using the documentation found here http://flask-sqlalchemy.pocoo.org/2.1/queries/ to help write this statement. I am getting an error for invalid syntax. The User class is set up like the "simple example" from http://flask-sqlalchemy.pocoo.org/2.1/models/#models but does not have the repr function.

Related

How to solve second order SQL injection attacks vulnerability?

I'm facing a challenge while deploying a backed application built using Python Flask. The codegate scan is catching up some of the code integrated in the CI/CD process as potential vulnerabilities. Below are 2 types of vulnerabilities.
Second order SQL injection.
SQL injection
I'm using raw SQL queries to get the data from the DB in conjunction with Flask SQLAlchemy and Pandas.
Below is a sample code where codegate is pointing the issues for Second order SQL injection.
def get_user_data(user_id: str):
query: str = USER_ID_QUERY.format(user_id=user_id)
result = db.session.execute(query)
result = result.fetchall()
if len(result) < 1:
raise Exception("User not Found")
return result[0][0]
Query
USER_ID_QUERY = """SELECT USER_ID FROM USER WHERE USER_ID = '{user_id}'"""
Vulnerability description - Method get_user_data at line 236 of
src\utils.py gets database data from the execute element. This
element’s value then flows through the code without being properly
sanitized or validated, and is eventually used in a database query in
method check_access at line 50 of src\service.py. This may enable an
Second-Order SQL Injection attack.
I have tried below solution after digging out the internet by still it is giving the same error.
Binding the parameters using text and bindparams of sqlalchemy
query = text(USER_ID_QUERY).bindparams(user_id=user_id)
Could someone please help me in highlighting what is wrong here or what can be done to resolve these painfull issues?

python pyodbc SQLite sql injections

I use pyodbc in my python flask Project for the SQLite DB connection.
I know and understand SQL Injections but this is my first time dealing with it.
I tried to execute some
I have a function which concatenates the SQL String in my database.py file:
def open_issue(self, data_object):
cursor = self.conn.cursor()
# data_object is the issue i get from the user
name = data_object["name"]
text = data_object["text"]
rating_sum = 0
# if the user provides an issue
if name:
# check if issue is already in db
test = cursor.execute(f'''SELECT name FROM issue WHERE name = "{name}"''')
data = test.fetchall()
# if not in db insert
if len(data) == 0:
# insert the issue
cursor.executescript(f'''INSERT INTO issue (name, text, rating_sum)
VALUES ("{name}", "{text}", {rating_sum})''')
else:
print("nothing inserted!")
In the api.py file the open_issue() function gets called:
#self.app.route('/open_issue')
def insertdata():
# data sent from client
# data_object = flask.request.json
# unit test dictionary
data_object = {"name": "injection-test-table",
"text": "'; CREATE TABLE 'injected_table-1337';--"}
DB().open_issue(data_object)
The "'; CREATE TABLE 'injected_table-1337';--" sql injection has not created the injected_table-1337, instead it got inserted normally like a string into the text column of the injection-test-table.
So i don't really know if i am safe for the standard ways of SQL injection (this project will only be hosted locally but good security is always welcome)
And secondary: are there ways with pyodbc to check if a string contains sql syntax or symbols, so that nothing will get inserted in my example or do i need to check the strings manually?
Thanks a lot
As it turns out, with SQLite you are at much less risk of SQL injection issues because by default neither Python's built-in sqlite3 module nor the SQLite ODBC driver allow multiple statements to be executed in a single .execute call (commonly known as an "anonymous code block"). This code:
thing = "'; CREATE TABLE bobby (id int primary key); --"
sql = f"SELECT * FROM table1 WHERE txt='{thing}'"
crsr.execute(sql)
throws this for sqlite3
sqlite3.Warning: You can only execute one statement at a time.
and this for SQLite ODBC
pyodbc.Error: ('HY000', '[HY000] only one SQL statement allowed (-1) (SQLExecDirectW)')
Still, you should follow best practices and use a proper parameterized query
thing = "'; CREATE TABLE bobby (id int primary key); --"
sql = "SELECT * FROM table1 WHERE txt=?"
crsr.execute(sql, (thing, ))
because this will also correctly handle parameter values that would cause errors if injected directly, e.g.,
thing = "it's good to avoid SQL injection"

how to insert python logs in postgresql table?

I need to insert the logs from my test case into a table in postgresql data base.
I was able to connect to the db but I can't figure out how to insert this line result in the tabble, I have tried the below but it doesnt work
import logging
import psycopg2
from io import StringIO
from config import config
params = config()
conn = psycopg2.connect(**params)
print(conn)
curr = conn.cursor()
try:
if not hw.has_connection():
logging.error('Failure: Unable to reach websb! ==> '+ str(driver.find_element_by_xpath('//span[#jsselect="heading" and #jsvalues=".innerHTML:msg"]').text))
return
elif hw.is_websiteReachable():
logging.info("Success: website is reachable!")
curr.execute("""INSERT INTO db_name(logs) VALUES (%s)""", ("Success: website is reachable!"))
conn.commit()
except:
logging.error("Failure: Unable to reach website!")
return
Iam a total beginner in this. I have searched but I couldnt find a clear example or guide about it. the above code throws the exception eventhough the website is reachable. sorry if I sound dumb.
It looks like you're incorrectly constructing your SQL statement. Instead of INSERT INTO db_name(table_name) ... it should be INSERT INTO table_name(column_name) .... If you've correctly connected to the appropriate database in your connection settings, you usually don't have to specify the database name each time you write your SQL.
Therefore I would recommend, the following modification (assuming your table is called logs and it has a column named message):
# ...
sql = 'INSERT INTO logs(message) VALUES (%s);'
msg = 'Success: website is reachable!'
curr.execute(sql, (msg))
conn.commit()
You can read the pyscopg2 docs here for more information as well if that would help with passing named parameters to your SQL queries in Python.
You can check a good solution that I personally use in my in-server projects. You just need to give a connection-string to the CRUD object and all the things will be done. For Postgres you can use:
'postgresql+psycopg2://username:password#host:port/database'
or
'postgresql+pg8000://username:password#host:port/database'
for more details check SQLAlchemy Engine Configuration.

OperationalError: no such column: XX - Sqlite

I'm using sqlite3 alongside python. I'm just trying to get some really basic auth going on but when my sql query gets executed I am getting the following error:
OperationalError: no such column: admin
My query looks like the following:
cur.execute("SELECT * FROM users WHERE username=%s AND password=%s;" % (username, password))
Any ideas? I have searched around but can't find anything
You should never use string interpolation in database queries; as well as the problem you're having, you leave yourself open to SQL injection attacks. Instead, use the parameter substitution provided by the db api.
cur.execute("SELECT * FROM users WHERE username=? AND password=?", (username, password))

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