
При работе с базами данных через Python ключевым моментом является безопасная и корректная передача переменных в SQL-запросы. Простая конкатенация строк повышает риск SQL-инъекций и ошибок при обработке специальных символов, таких как апострофы или кавычки.
Оптимальный подход – использование параметризованных запросов через библиотеки sqlite3, psycopg2 или MySQL Connector. Например, в sqlite3 вместо формирования строки вида "SELECT * FROM users WHERE id=" + user_id применяется синтаксис cursor.execute("SELECT * FROM users WHERE id=?", (user_id,)), где знак вопроса служит placeholder для значения переменной.
При работе с несколькими переменными рекомендуется передавать их как кортеж или словарь, что упрощает масштабирование запросов. В psycopg2 поддерживается формат %(имя)s, позволяющий использовать читаемые ключи: cursor.execute("SELECT * FROM orders WHERE user_id=%(uid)s AND status=%(st)s", {"uid": user_id, "st": status}).
Использование таких методов не только повышает безопасность, но и обеспечивает правильное экранирование данных и поддержку типов, включая даты и булевы значения. Это особенно важно при интеграции Python с крупными базами данных, где некорректная передача переменных может привести к потере данных или сбоям в работе приложения.
Использование параметров в sqlite3 для безопасной подстановки

В sqlite3 подстановка переменных напрямую в строку SQL создаёт риск SQL-инъекций. Вместо конкатенации строк рекомендуется использовать параметризованные запросы.
Простейший способ – использовать символы вопроса ? для позиционных параметров:
import sqlite3
conn = sqlite3.connect('example.db')
cursor = conn.cursor()
username = 'alice'
cursor.execute("SELECT * FROM users WHERE username = ?", (username,))
results = cursor.fetchall()
Особенности использования:
- Передавать параметры следует как кортеж или список, даже если один элемент.
- sqlite3 автоматически экранирует строки, предотвращая SQL-инъекции.
- Использование
cursor.execute()с параметрами сохраняет производительность за счёт подготовленных выражений.
Для именованных параметров применяются двоеточие и словарь:
cursor.execute(
"INSERT INTO users (username, age) VALUES (:name, :age)",
{"name": "bob", "age": 30}
)
- Именованные параметры удобны при множестве значений или повторяющихся полях.
- Словарь позволяет явно связывать имена параметров с переменными Python.
Для массовой вставки данных используется executemany():
data = [("charlie", 25), ("diana", 28)]
cursor.executemany(
"INSERT INTO users (username, age) VALUES (?, ?)",
data
)
conn.commit()
Рекомендации:
- Никогда не формируйте SQL через конкатенацию строк с пользовательским вводом.
- Используйте позиционные
?или именованные:paramпараметры в зависимости от задачи. - Для повторяющихся операций с разными данными применяйте
executemany()для повышения скорости и безопасности.
Форматирование строк с f-string в простых запросах
F-string позволяет напрямую вставлять значения переменных Python в SQL-запрос, сохраняя читаемость кода. В простых запросах, где нет пользовательского ввода, это удобно для быстрого прототипирования или работы с фиксированными данными.
Пример использования f-string с константами:
user_id = 42
query = f"SELECT * FROM users WHERE id = {user_id}"
Важно учитывать, что f-string автоматически преобразует числовые и строковые переменные, но строки должны быть заключены в кавычки внутри запроса:
username = 'ivan'
query = f"SELECT * FROM users WHERE name = '{username}'"
Для вставки нескольких переменных удобно использовать кортежи или словари:
user_id = 42
status = 'active'
query = f"SELECT * FROM users WHERE id = {user_id} AND status = '{status}'"
Следует избегать использования f-string с прямым пользовательским вводом, чтобы не допустить SQL-инъекцию. Если данные фиксированы или полностью контролируются, f-string обеспечивает минимальный синтаксический шум и высокую читаемость.
| Тип данных | Пример вставки |
|---|---|
| Число | {variable} |
| Строка | '{variable}' |
| Дата | '{variable:%Y-%m-%d}' |
| Булево | {int(variable)} |
Использование f-string упрощает отладку: переменные легко отслеживаются при печати запроса, а синтаксис остаётся компактным. При работе с простыми, внутренними запросами это снижает количество ошибок и ускоряет разработку.
Подстановка переменных через %s в psycopg2
В psycopg2 для безопасной передачи значений в SQL-запросы используется синтаксис %s, независимо от типа данных переменной. Это предотвращает SQL-инъекции и упрощает работу с параметрами.
Пример запроса с подстановкой одного значения:
cur.execute("SELECT * FROM users WHERE id = %s", (user_id,))
Важно: переменные передаются в виде кортежа или списка, даже если это один элемент. Отсутствие запятой после одного элемента приведет к ошибке.
Для множественной подстановки: cur.execute("INSERT INTO products(name, price) VALUES (%s, %s)", (name, price)). Порядок переменных в кортеже должен соответствовать порядку %s в SQL.
Не рекомендуется использовать % из Python для форматирования строк в SQL. psycopg2 самостоятельно экранирует специальные символы, включая апострофы и кавычки.
Для списков значений, например, при условии WHERE id IN (…), используется адаптация через psycopg2.sql или execute_values из psycopg2.extras, так как простой кортеж через %s не подставляется напрямую в IN.
Использование %s совместимо со всеми стандартными типами PostgreSQL: строки, числа, даты, булевы значения. psycopg2 автоматически преобразует Python-объекты в соответствующие SQL-типы.
Для динамических запросов с большим числом параметров рекомендуется предварительно подготовить кортеж или список и передавать его целиком. Это повышает читаемость и снижает риск ошибок при изменении числа колонок.
Передача нескольких значений через tuple или list

Для передачи нескольких значений в SQL-запрос через Python удобно использовать кортежи (tuple) или списки (list). В библиотеке `sqlite3` или `psycopg2` это реализуется с помощью параметризованных запросов, что предотвращает SQL-инъекции.
Пример с tuple для выборки пользователей по нескольким ID:
ids = (1, 2, 5, 8)
cursor.execute("SELECT * FROM users WHERE id IN (%s)" % ','.join(['%s'] * len(ids)), ids)
Для списка синтаксис аналогичен:
ids_list = [3, 4, 7]
cursor.execute("SELECT * FROM users WHERE id IN (%s)" % ','.join(['%s'] * len(ids_list)), ids_list)
Ключевой момент – количество плейсхолдеров (`%s` или `?`) должно совпадать с количеством элементов в tuple или list. Использование `.join()` обеспечивает динамическую генерацию нужного числа плейсхолдеров.
Передача коллекций через tuple или list также применима к вставке нескольких значений в `INSERT`:
values = [(1, 'Alice'), (2, 'Bob')]
cursor.executemany("INSERT INTO users (id, name) VALUES (%s, %s)", values)
Рекомендация: всегда использовать параметризованные запросы, даже при передаче нескольких элементов, чтобы исключить возможность SQL-инъекций и упростить поддержку кода.
Обработка строк с кавычками и спецсимволами в запросах

При вставке строковых значений в SQL через Python критично учитывать наличие одиночных и двойных кавычек, а также символов обратной косой черты. Неправильная обработка этих символов приводит к синтаксическим ошибкам и уязвимостям типа SQL-инъекций.
Используйте параметризованные запросы с библиотеками, поддерживающими плейсхолдеры, например: cursor.execute("INSERT INTO users (name) VALUES (%s)", (name,)) для psycopg2 или cursor.execute("INSERT INTO users (name) VALUES (?)", (name,)) для sqlite3. Это автоматически экранирует все спецсимволы.
Если параметризация невозможна, применяйте встроенные функции экранирования. В MySQL через Python можно использовать pymysql.escape_string(value), в SQLite – функцию sqlite3.Connection.escape(value) (в новых версиях рекомендуется использовать только параметризацию).
Не пытайтесь вручную добавлять слэши или кавычки для экранирования: разные СУБД интерпретируют спецсимволы по-разному. Например, в PostgreSQL одиночная кавычка экранируется как двойная: 'O''Reilly', в MySQL – через обратную косую черту: 'O\'Reilly'.
Регулярная проверка строк перед вставкой помогает выявлять неожиданные символы. Используйте функции Python str.replace() или регулярные выражения для нормализации, но только как дополнительный слой, не заменяющий параметризацию.
Для комплексных данных, содержащих JSON или HTML, храните строки в бинарном формате или используйте специальные колонки типа JSONB в PostgreSQL. Это снижает риск нарушений синтаксиса при наличии кавычек, скобок и управляющих символов.
Использование named placeholders для удобства и читаемости
Named placeholders позволяют вставлять значения переменных в SQL-запросы с указанием имени параметра вместо позиции. Это повышает читаемость и снижает риск ошибок при изменении порядка аргументов.
Пример синтаксиса с библиотекой sqlite3:
query = "SELECT * FROM users WHERE username = :username AND age > :age"
params = {"username": "ivan", "age": 25}
cursor.execute(query, params)
Основные преимущества:
| Преимущество | Описание |
|---|---|
| Ясность | Каждое значение привязано к имени, легко понять, что вставляется в какой столбец. |
| Гибкость | Добавление или удаление параметров не требует перестановки других значений. |
| Безопасность | Использование named placeholders предотвращает SQL-инъекции за счёт автоматического экранирования значений. |
| Поддержка разных СУБД | Большинство библиотек Python для SQL, включая psycopg2 и sqlite3, поддерживают named placeholders. |
Рекомендации по использованию:
- Всегда используйте словарь для передачи параметров, чтобы имена совпадали с плейсхолдерами.
- Избегайте смешивания позиционных и именованных параметров в одном запросе.
- Для сложных запросов с множеством условий именованные параметры повышают читаемость и упрощают отладку.
- При работе с повторяющимися значениями используйте одно имя параметра несколько раз – достаточно указать его в словаре один раз.
Пример с повторяющимися параметрами:
query = "SELECT * FROM orders WHERE customer_id = :cid OR referrer_id = :cid"
params = {"cid": 42}
cursor.execute(query, params)
Передача переменных в INSERT и UPDATE запросах
В Python переменные можно безопасно передавать в SQL-запросы с помощью параметризации. Это исключает SQL-инъекции и упрощает работу с динамическими данными.
Пример передачи переменных в INSERT с использованием библиотеки sqlite3:
import sqlite3
conn = sqlite3.connect('database.db')
cursor = conn.cursor()
name = "Иван"
age = 30
cursor.execute("INSERT INTO users (name, age) VALUES (?, ?)", (name, age))
conn.commit()
conn.close()
В UPDATE-запросах подход аналогичен:
cursor.execute(
"UPDATE users SET age = ? WHERE name = ?",
(31, "Иван")
)
conn.commit()
Рекомендации при передаче переменных:
- Использовать плейсхолдеры (
?для SQLite,%sдля MySQL/PostgreSQL), а не форматирование строк. - Передавать значения в виде кортежей или словарей в зависимости от библиотеки.
- Проверять соответствие типов данных: строки, числа, даты должны совпадать с типами в таблице.
- Для нескольких строк использовать
executemany()для оптимизации вставки. - Не вставлять динамически имена таблиц и колонок через переменные; только значения.
Пример массовой вставки нескольких записей:
users = [("Анна", 25), ("Пётр", 28)]
cursor.executemany("INSERT INTO users (name, age) VALUES (?, ?)", users)
conn.commit()
Следуя этим правилам, INSERT и UPDATE-запросы будут безопасными, предсказуемыми и легко масштабируемыми при работе с Python и SQL.
Избежание SQL-инъекций при динамическом формировании запросов

При динамическом формировании SQL-запросов использование строковой конкатенации с переменными повышает риск SQL-инъекций. Вместо этого применяйте параметризованные запросы с плейсхолдерами. В библиотеке sqlite3 это выглядит как `cursor.execute(«SELECT * FROM users WHERE id = ?», (user_id,))`, где `?` заменяется безопасно перед выполнением запроса.
Для MySQL через модуль `mysql.connector` используйте синтаксис `%s`: `cursor.execute(«SELECT * FROM users WHERE email = %s», (email,))`. Передача значений через кортеж предотвращает внедрение вредоносного SQL-кода.
Избегайте вставки переменных напрямую в строки с помощью f-строк или оператора `+`. Любая пользовательская строка, включенная напрямую, может завершить запрос или вставить дополнительные команды.
При работе с динамическими идентификаторами таблиц или колонок используйте строгую валидацию: проверяйте соответствие значения заранее определённому списку допустимых названий. Прямое включение переменных в такие элементы SQL без проверки недопустимо.
Для комплексных запросов с множественными параметрами применяйте именованные плейсхолдеры (`:name`) и передавайте словарь значений. Это упрощает чтение кода и снижает риск ошибок при сопоставлении параметров.
Регулярно проводите аудит запросов: проверяйте все места динамического формирования SQL и заменяйте конкатенацию на параметризованные конструкции. Использование ORM также уменьшает вероятность инъекций, но не отменяет необходимости проверки пользовательских данных перед передачей в запрос.
Вопрос-ответ:
Как безопасно подставлять переменные в SQL-запросы в Python?
Наиболее безопасный способ — использовать параметры запроса через библиотеку для работы с базой данных. Например, в sqlite3 это делается с помощью знака вопроса (?), а в psycopg2 для PostgreSQL — с помощью %s. Такой подход предотвращает SQL-инъекции, так как библиотека самостоятельно экранирует значения переменных перед выполнением запроса.
Можно ли просто соединять строки с переменной для создания SQL-запроса?
Технически это возможно, но крайне нежелательно. Соединение строк через + или f-строки может привести к SQL-инъекциям, если значение переменной поступает от пользователя. Например, ввод «’; DROP TABLE users;—» может удалить таблицу. Всегда лучше использовать подготовленные выражения с параметрами.
Как вставить несколько переменных одновременно в один SQL-запрос?
Большинство библиотек поддерживают передачу нескольких параметров в виде кортежа или списка. Например, в sqlite3 можно написать: cursor.execute(«INSERT INTO users (name, age) VALUES (?, ?)», (username, userage)). Библиотека автоматически подставит значения на места ?, обеспечив корректное экранирование и сохранение типов данных.
Как работать с переменными, если нужно использовать их в операторе LIKE?
При использовании LIKE переменная может содержать символы подстановки, такие как %, _. Чтобы корректно подставить переменную, стоит передавать её как параметр запроса, добавляя нужные символы внутри Python-строки: cursor.execute(«SELECT * FROM products WHERE name LIKE ?», (‘%’ + search_term + ‘%’,)). Такой подход сохраняет безопасность и корректность запроса.
