Как получить имена таблиц от пользователя в pyodbc, избегая SQL-инъекций? - PullRequest
1 голос
/ 25 апреля 2019

У меня есть следующий код, который полностью безопасен, и пользователь не может вводить что-либо, что может привести к внедрению SQL.

value = get_value_from_user_input()
query = \
    """
    SELECT *
    FROM TestTable
    WHERE CompareValue = ?
    """
cursor.execute(query, value)

Но что, если мне понадобится получить имя таблицы от пользователя.Я не могу сделать это так, потому что это делает меня уязвимым для SQL-инъекций;

table_name = get_table_name_from_user_input()
query = \
    f"""
    SELECT *
    FROM {table_name}
    WHERE CompareValue = 1234
    """
cursor.execute(query)

И я также не могу сделать это так (что было бы логичным imo), потому что это выдает ошибку.

table_name = get_table_name_from_user_input()
query = \
    """
    SELECT *
    FROM ?
    WHERE CompareValue = 1234
    """
cursor.execute(query, table_name)

Так как мне это сделать?

Дополнительная информация:

Программа предназначена для использования внутри моей компании и всех пользователейПрограммное обеспечение имеет права администратора на базу данных.Возможно, было бы бессмысленно запретить им вводить SQL, так как они в любом случае могут выполнять необработанные запросы SQL.Я просто хочу быть педантичным и не позволять моей программе выполнять произвольный SQL.get_table_name_from_user_input() фактически получает имя таблицы из файла конфигурации, который описывает структуру базы данных, поэтому пользователь может легко отредактировать ее, если когда-нибудь имена таблиц изменятся в базе данных, без необходимости трогать исходный код.

Ответы [ 2 ]

1 голос
/ 25 апреля 2019

Вы можете получить список допустимых имен таблиц непосредственно из базы данных, используя функцию Cursor # таблицы pyodbc :

crsr = cnxn.cursor()
table_names = [x[2] for x in crsr.tables(tableType='TABLE')]
print(table_names)  # ['customer', 'invoice', ...]

Как вы уже заметили, вы не можете использоватьпараметры для предоставления объекта (например, таблицы или столбца) names в запрос, но вы можете использовать функцию T-SQL QUOTENAME , чтобы гарантировать, что (динамический) SQL, который вы создаете, являетсядопустимо.

Хорошо, что вы ищете возможные проблемы с SQL-инъекцией, но не весь динамический SQL-это зло.Иногда, как в этом случае, это необходимо;вам просто нужно принять соответствующие меры, чтобы защитить себя.

0 голосов
/ 25 апреля 2019

Если у вас не слишком много таблиц, вы можете проверить, существует ли запрошенное имя таблицы в списке допустимых имен таблиц.

tables = ["customers", "vendors", "products"]

user_inputs = ["products", "invoices", "vendors WHERE CompareValue=1234; DROP TABLE customers; --"]

query = "SELECT * FROM placeholder WHERE CompareValue=1234;"

for user_input in user_inputs:
  if user_input in tables:
    print(query.replace("placeholder", user_input))
  else:
    print(f"'{user_input}' is not a valid table")

Дает следующие результаты

SELECT * FROM products WHERE CompareValue=1234;
'invoices' is not a valid table
'vendors WHERE CompareValue=1234; DROP TABLE customers; --' is not a valid table

Этот фрагмент в ответе: https://repl.it/repls/SeveralAnimatedAutomaticvectorization

...