Can I replace a column name with a placeholder in while querying data from a table in sqlite?
Example:
db.execute("SELECT ? FROM currency WHERE user_id = ?", ( "usd", 1).fetchall()
It returns the placeholder which is 'usd" or any string I replaced it with.
Can I replace a column name with a placeholder in while querying data from a table in sqlite
No.
Placeholders exist to separate data (i.e. values) from code (i.e. the structure and meaning of the SQL statement). This way placeholders prevent SQL injection vulnerabilities.
Column names are code in SQL. If it was possible to parameterize them, the data/code separation would break, making your SQL vulnerable to injection attacks again, defeating the whole point of placeholders.
Also, you should not want to. It indicates a design flaw in your database if that appears as something you need. If your column name is variable, it should probably be a column itself.
So this
SELECT ? FROM currency WHERE user_id = ?
should probably be
SELECT data FROM currency WHERE user_id = ? AND currency_symbol = ?
Related
When I'm trying to format an SQL statement this way:
cursor.execute('SELECT (%s) FROM table WHERE id = 12345', (column,))
it doesn't work properly. Instead of returning values from the specified column, it just returns the name of the column. Any way to fix that?
It only works with formatted string, but according to reviews it is not the safest approach
You can't bind column names like that -you're binding a string literal with that name.
If you want to dynamically set the column names, you'll have to resort to string manipulation of some sort, e.g.:
cursor.execute(f'SELECT {column} FROM table WHERE id = 12345')
Of course if column is created by user-input, you'll have to sanitize it somehow.
I have a database with 2 tables: students, employees and I want to update one of those tables:
import sqlite3
db_file = "school.db"
def update_address(identifier, user_address, user_id):
with sqlite3.connect(db_file) as conn:
c = conn.cursor()
c.execute(f"""
UPDATE {identifier}
SET address = ?
WHERE id = ?;
""",
(user_address, user_id))
update_address("students", "204 Sycamore Street", 2)
The above code works, the problem is I know that using python string formatting in an sql operation can lead to vulnerabilities per sqlite3 docs:
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 https://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.
The placeholder '?' works when it comes to inserting values but not for sql identifiers. Output:
sqlite3.OperationalError: near "?": syntax error
So the question here is: can an sql injection occur if I use python string formatting on an sql identifier or does it only occur on values ?
If it also occurs on identifiers is there a way to format the string in a safe manner?
Yes, if you interpolate any content into an SQL query unsafely, it is an SQL injection vulnerability. It doesn't matter if the content is supposed to be used as a value in the SQL expression, or an identifier, SQL keyword, or anything else.
It's pretty common to format queries from fragments of SQL expressions, if you want to write a query with a variable set of conditions. These are also possible SQL injection risks.
The way to mitigate the SQL injection risk is: don't interpolate untrusted input into your SQL query.
For identifiers, you should make sure the content matches a legitimate name of a table (or column, or other element, if that's what you're trying to make dynamic). I.e. create an "allowlist" of tables known to exist in your database that are permitted to update using your function. If the input doesn't match one of these, then don't run the query.
It's also a good idea to use back-ticks to delimit identifiers, because if one of the table names happens to be a reserved keyword in SQLite, that will allow the table to be used in the SQL query.
if identifier not in ["table1", "table2", "table3"]:
raise Exception("Unknown table name: '{identifier}'")
c.execute(f"""
UPDATE `{identifier}`
SET address = ?
WHERE id = ?;
""",
(user_address, user_id))
I have many tables filled with rows and I want to be able to pass in input strings as variables to the query, I have tried many things and research but can't figure it out. Here is the code
def find_model(model,name):
c.execute('''SELECT ? FROM ?''')#,(model,name)
rows = c.fetchall()
for row in rows:
print(row)
find_model(2610-48,2610-48)
qmark (?) style queries only supported for parameters but not for table name or column list.
For substituting column list or table name you can use all formatting related features, for example:
f string - f"select {columns} from {table}" where columns and table are vaiables in current scope
% string formatting - "select %s from %s" % ("a,b,c", "super_table")
But never use anything from this features for parameters in update/create queries, specially for user-passed data. Always use qmark syntax to ensure no sql injections can be performed. For example: cur.execute("insert into test values (?, ?)", ("a", 123))(docs).
Quick summary:
never substitute untrusted user input via string formatting to query
you can substitute columns and table names to query using formatting features of python, but do this only for trusted input values
always use qmark style queries form parameters
I am trying to load the data inside the table trial and it says Invalid Column name - Name.
I am passing values inside Name and Area dynamically.
cursor.execute("insert into trial (NameofTheProperty, AreaofTheProperty)
values (Name, Area)")
cnxn.commit()
You need to have quotes around the column values so that they are not gonna be interpreted as column names instead:
insert into
trial (NameofTheProperty, AreaofTheProperty)
values
("Name", "Area")
Now, since you mentioned that you dynamically insert these values into the query, you can just let your database driver handle the quotes and other things like type conversions:
property_name = "Name"
property_area = "Area"
cursor.execute("""
insert into
trial (NameofTheProperty, AreaofTheProperty)
values
(?, ?)""", (property_name, property_area))
cnxn.commit()
This is called query parameterization and is considered the safest and the most robust way to insert values into the SQL queries. These ? values are called "placeholders".
Note that the database driver is gonna put quotes around the string values automatically - no need to do it manually.
I'm trying to insert a record into an sqlite database using named parameters in python (with the sqlite3 module).
The values I want to insert are in a dictionary, but the dictionary keys might contain dashes, for example {'request-id': 100, 'year': '2015'}.
I'm trying to execute the following:
import sqlite3
conn = sqlite3.connect('database.db')
cursor = conn.cursor()
cursor.execute('''CREATE TABLE IF NOT EXISTS requests (request_id text, year text)''')
query = '''INSERT INTO requests (request_id, year) VALUES (:request-id, :year)'''
cursor.execute(query, {'request-id': 100, 'year': '2015'})
conn.commit()
conn.close()
I get this error during the insert statement:
sqlite3.OperationalError: no such column: id
It seems like dashes are not well accepted as named parameters.
There are many workarounds for this, like creating a new dictionary where dashes in the keys are replaced by underscores, but I'd like to know if I could use some escaping technique or something else to avoid that.
Thanks for your help
The documentation for sqlite3_bind_* states that parameter names must be composed of alphanumeric characters, and doesn't mention a way of escaping them.
Your query is probably being parsed as :request - id, i.e. :request minus id, and since there's no such column id, SQLite throws an error.
(Also, as Prerak Sola points out, you create the table with a date column but try to insert to a year column which doesn't exist.)
SQL parameter names have no quoting or escaping mechanism; you have to use the same rules as for an unquoted identifier.