flawopen.com/SQL Injection/Достаточно ли экранирования?
Защищает ли экранирование кавычек от SQL Injection?
Обычно нет. Функция экранирования зависит от СУБД, кодировки соединения, режима SQL и точного контекста кавычек. Параметризованные запросы отделяют значения от синтаксиса SQL через API драйвера. Значения передавайте параметрами; динамические идентификаторы, которые нельзя параметризовать, выбирайте из фиксированного списка приложения.
Почему экранирование ненадёжно на практике
Экранирование — не контекстно-независимая замена текста. MySQL указывает, что mysql_real_escape_string() использует кодировку соединения, а SET NAMES не обновляет состояние клиентской библиотеки, которое использует эта функция. Несовпадение кодировок может нарушить предположения о защищённом тексте. Предпочитайте параметризованные API; если старый код пока требует экранирования, применяйте поддерживаемую функцию драйвера с правильно настроенной кодировкой и обрабатывайте ошибки.
Экранирование нужно корректно применять при каждой интерполяции с учётом драйвера, кодировки, режима SQL и контекста литерала. Параметризованные API явно отделяют значение от данных в вызове запроса и избавляют от ручной сборки строки в кавычках.
Экранирование кавычек затрагивает только символы внутри конкретного строкового литерала. Оно не создаёт безопасные имена таблиц и столбцов, ключевые слова или направление сортировки и не защищает ввод в некавыченных числовых выражениях. Параметризуйте значения, а динамическую структуру выбирайте из фиксированных вариантов приложения.
// Пользовательский ввод вставляется в синтаксис SQL
query = f"SELECT id FROM users WHERE name = '{name}'"
cursor.execute(query)
// Драйвер связывает name как значение данных
cursor.execute(
"SELECT id FROM users WHERE name = ?",
(name,),
)
Что меняет привязка параметров
Параметризованный запрос передаёт структуру SQL и значения как отдельные входы через API базы данных. Драйвер может использовать нативные подготовленные запросы или другую безопасную реализацию; не предполагайте конкретную последовательность протокола. Важно, что значение считается данными, а не повторно разбирается как синтаксис SQL. В Python DB-API используйте плейсхолдер драйвера и передавайте значения отдельно; не формируйте запрос строковым форматированием.
Инженерный контрольный список
- ✓Передавайте каждое пользовательское значение через API привязки параметров; не форматируйте его в текст SQL.
- ✓Выбирайте динамические имена таблиц, столбцов и сортировки из фиксированных списков приложения.
- ✓Если старое экранирование пока нельзя удалить, используйте контекстную функцию драйвера и проверьте кодировку и режим SQL соединения.
- ✓Проверяйте запросы на кавычках, не-ASCII тексте и неверных идентификаторах, не выполняя разрушительные команды.
Частые вопросы
Для идентификаторов или SQL-фрагментов, которые нельзя передать параметром, используйте только поддерживаемую драйвером функцию quoting вместе с фиксированным списком приложения. Не применяйте общее экранирование к произвольным значениям.
Она может помочь в документированном контексте, но зависит от СУБД, настроек соединения, контекста кавычек и правильного применения в каждом месте. Параметризованные запросы остаются более безопасным вариантом по умолчанию для значений.
Источники
- OWASP — руководство по предотвращению SQL Injection
- CWE-89 — SQL Injection
- Руководство PHP — mysqli::real_escape_string
- MySQL C API — mysql_real_escape_string
- flawopen.com — SQL Injection (все языки)