Я использую PyMySQL, и когда я пишу запросы, мне нужно избегать определенных идентификаторов, предоставленных пользователем (имена таблиц и т. д.). Например, вот плохой пример, показывающий, что я имею в виду:
sql = "SELECT * FROM {}".format(table_name)
cursor.execute(sql)
В этом случае table_name предоставляется пользователем, поэтому приведенный выше код явно уязвим для атаки с использованием SQL-инъекции.
Сначала я попробовал сделать что-то вроде этого:
sql = "SELECT * FROM %s"
cursor.execute(sql, (table_name,))
Кажется, что приведенный выше код не работает при экранировании идентификаторов, он работает только со значениями. Это не удивительно, потому что я видел подобное поведение в других библиотеках MySQL на других языках.
Я использовал другие библиотеки, такие как mysql в Node.js, в которых есть функция для экранирования идентификаторов. Я не вижу ничего в Документация PyMySQL, что указывает на аналогичную функциональность. Я обнаружил, что этот StackOverflow вопрос задает аналогичный вопрос, но ответ был примерно таким: «Вам не нужно этого делать». Что ж, мне нужно это сделать! ;)
Какие у меня есть варианты экранирования идентификаторов при использовании MySQL в Python? Спасибо!






Драйвер python имитирует параметры запроса, но следует аналогичным правилам. Заполнитель, такой как %s, будет заменен строкой цитируется, при этом строка будет экранирована, поэтому в любые буквальные символы апострофа будет вставлен \.
sql = "SELECT * FROM mytable WHERE name = %s"
cursor.execute(sql, (myname,))
Результатом будет SQL:
SELECT * FROM mytable WHERE name = 'O\'Reilly'
Обратная косая черта необходима для предотвращения внедрения SQL.
Но это бесполезно для имен таблиц, потому что одинарные кавычки предназначены для строковых литералов или литералов даты, а не для идентификаторов.
Вы можете сделать это так:
sql = "SELECT * FROM `{}`".format(table_name)
Обратные галочки ограничивают идентификатор, поэтому вы можете использовать зарезервированные слова, пробелы или другие специальные символы в имени вашей таблицы.
Вы должны убедиться, что значение table_name не включает в себя буквальные символы обратной галочки, или же вставьте обратную косую черту, если у вас есть буквальная обратная косая черта в имени таблицы.
Расширяя ответ от @ Билл Карвин, вам нужно будет самостоятельно избегать обратных кавычек, потому что pymysql не делает этого изначально, даже его метод escape_string().
Однако, вопреки тому, что сказал Билл, чтобы избежать возможных обратных кавычек в имени таблицы, вам нужно будет их удвоить, а не использовать \. От документация mysql:
Identifier quote characters can be included within an identifier if you quote the identifier. If the character to be included within the identifier is the same as that used to quote the identifier itself, then you need to double the character.
Это в основном означает, что для того, чтобы избежать обратной кавычки, вам понадобится двойная обратная кавычка. Вот к чему я в итоге прибег:
table = 'table`; drop table users; -- '
sane_table = pymysql.escape_string(table).strip('`').replace('`', '``')
cursor.execute(f"SELECT * FROM `{sane_table}`"))
Что будет правильно экранировать имя таблицы и сообщать правильную ошибку:
pymysql.err.ProgrammingError: (1103, "Incorrect table name 'table`; drop table users; -- '")
«Это не удивительно, потому что я видел подобное поведение в других библиотеках MySQL на других языках». Затем это моделируется фреймворком. Собственный протокол подготовки MySQL позволяет подготавливать только значения, а не имена баз данных, таблиц или столбцов.