Разберите следующий SQL-кейс: "SELECT * FROM users WHERE name = '" + username + "';" — какие уязвимости есть, как их избежать и когда пригодится применить подготовленные выражения
Коротко и по делу. 1) В чём уязвимость - Конкатенация строки запроса с внешним вводом делает запрос уязвимым к SQL‑инъекции. Пример уязвимого кода: SELECT * FROM users WHERE name = ' " + username + " '; Если в username попадёт, например, "' OR 1=11=11=1 --", то запрос превратится в tautology и вернёт все строки. - Виды атак: классическая/union‑инъекция, булева/слепая, time‑based, error‑based, second‑order (ввод сохраняется и используется позже), comment/stacked queries (зависит от СУБД). - Последствия: утечка данных, обход аутентификации, изменение/удаление данных, выполнение административных команд, отказ в обслуживании. 2) Как избежать (рекомендуемый порядок) - Всегда использовать подготовленные выражения (parameterized queries / prepared statements / bind parameters). Они отделяют код от данных и исключают интерпретацию пользовательского ввода как SQL. Примеры: - MySQL (Node.js mysql): connection.query("SELECT * FROM users WHERE name = ?", [username]); - PostgreSQL (psycopg2, Python): cur.execute("SELECT * FROM users WHERE name = %s", (username,)) - Java (JDBC): PreparedStatement ps = conn.prepareStatement("SELECT * FROM users WHERE name = ?"); ps.setString(1, username); - Если подготовленные выражения невозможны: - Использовать проверенное экранирование/фиксацию (DB‑specific escaping functions) — временное решение. - Применять белые списки/валидацию для частей SQL, которые не могут быть параметризованы (имена таблиц/полей, ORDER BY): сравнивать с заранее допустимым набором. - Минимизация прав: аккаунт БД для приложения — с минимальными правами (право только SELECT/INSERT/UPDATE, только нужные таблицы). - Принцип наименьших данных: возвращать и запрашивать только необходимые поля, лимитировать результаты (LIMIT). - Дополнительно: использовать ORM/Query builder (правильно настроенные), WAF в критичных сценариях, логирование и мониторинг подозрительных запросов, правильная настройка кодировок/charset. 3) Когда применять подготовленные выражения - Всегда, когда в запрос попадает пользовательский ввод (параметры фильтрации, формы, заголовки, куки и т. п.). - Также полезно для повышения производительности при повторном выполнении одного и того же запроса с разными параметрами (параметризация позволяет СУБД кэшировать план). - Не решают задачу для динамической подстановки идентификаторов (таблицы/столбцы): там — белый список/валидация. 4) Практические замечания/подводные камни - Не полагайтесь только на клиентскую валидацию. - Подготовленные выражения защищают от синтаксической инъекции, но хранящиеся процедуры или ORM могут быть уязвимы, если внутри формируют SQL динамически. - Следите за кодировками (UTF‑7/UTF‑8 обходы), экранированием и побочными каналами (например, ошибки, тайминги). - Тестируйте: автоматизированные сканеры, ручное тестирование (payloads), code review. Вывод: основной и обязательный способ защиты — параметризованные запросы (prepared statements). Дополнительно — белые списки для идентификаторов, минимизация прав и валидация ввода.
1) В чём уязвимость
- Конкатенация строки запроса с внешним вводом делает запрос уязвимым к SQL‑инъекции. Пример уязвимого кода:
SELECT * FROM users WHERE name = ' " + username + " ';
Если в username попадёт, например, "' OR 1=11=11=1 --", то запрос превратится в tautology и вернёт все строки.
- Виды атак: классическая/union‑инъекция, булева/слепая, time‑based, error‑based, second‑order (ввод сохраняется и используется позже), comment/stacked queries (зависит от СУБД).
- Последствия: утечка данных, обход аутентификации, изменение/удаление данных, выполнение административных команд, отказ в обслуживании.
2) Как избежать (рекомендуемый порядок)
- Всегда использовать подготовленные выражения (parameterized queries / prepared statements / bind parameters). Они отделяют код от данных и исключают интерпретацию пользовательского ввода как SQL.
Примеры:
- MySQL (Node.js mysql):
connection.query("SELECT * FROM users WHERE name = ?", [username]);
- PostgreSQL (psycopg2, Python):
cur.execute("SELECT * FROM users WHERE name = %s", (username,))
- Java (JDBC):
PreparedStatement ps = conn.prepareStatement("SELECT * FROM users WHERE name = ?");
ps.setString(1, username);
- Если подготовленные выражения невозможны:
- Использовать проверенное экранирование/фиксацию (DB‑specific escaping functions) — временное решение.
- Применять белые списки/валидацию для частей SQL, которые не могут быть параметризованы (имена таблиц/полей, ORDER BY): сравнивать с заранее допустимым набором.
- Минимизация прав: аккаунт БД для приложения — с минимальными правами (право только SELECT/INSERT/UPDATE, только нужные таблицы).
- Принцип наименьших данных: возвращать и запрашивать только необходимые поля, лимитировать результаты (LIMIT).
- Дополнительно: использовать ORM/Query builder (правильно настроенные), WAF в критичных сценариях, логирование и мониторинг подозрительных запросов, правильная настройка кодировок/charset.
3) Когда применять подготовленные выражения
- Всегда, когда в запрос попадает пользовательский ввод (параметры фильтрации, формы, заголовки, куки и т. п.).
- Также полезно для повышения производительности при повторном выполнении одного и того же запроса с разными параметрами (параметризация позволяет СУБД кэшировать план).
- Не решают задачу для динамической подстановки идентификаторов (таблицы/столбцы): там — белый список/валидация.
4) Практические замечания/подводные камни
- Не полагайтесь только на клиентскую валидацию.
- Подготовленные выражения защищают от синтаксической инъекции, но хранящиеся процедуры или ORM могут быть уязвимы, если внутри формируют SQL динамически.
- Следите за кодировками (UTF‑7/UTF‑8 обходы), экранированием и побочными каналами (например, ошибки, тайминги).
- Тестируйте: автоматизированные сканеры, ручное тестирование (payloads), code review.
Вывод: основной и обязательный способ защиты — параметризованные запросы (prepared statements). Дополнительно — белые списки для идентификаторов, минимизация прав и валидация ввода.