SQL-инъекции: как возникают и как защититься

SQL-инъекция возникает, когда пользовательский ввод смешивается с текстом запроса и меняет его смысл.

SQL-инъекция — уязвимость, при которой непроверенные данные пользователя становятся частью SQL-запроса и изменяют его логику.

Откуда берётся проблема

Корень зла — склейка строк. Когда запрос собирают конкатенацией с пользовательским вводом, граница между «командой» и «данными» стирается. Рассмотрим уязвимый псевдокод (это иллюстрация проблемы, не запускаемый блок):

// ОПАСНО: ввод подставлен прямо в текст запроса
login = ввод_пользователя
query = "SELECT * FROM users WHERE name = '" + login + "'"

Если в поле login пользователь введёт обычное имя — всё хорошо. Но если ввод содержит кавычку и продолжение SQL, он «вырвется» из строкового литерала и допишет свою логику в запрос. Так непредусмотренный ввод превращается в исполняемую команду к базе. Принцип угрозы важно понимать, чтобы её закрыть.

К чему это приводит

Через такую дыру можно прочитать чужие данные, обойти проверку пароля или повредить таблицы. Это одна из старейших и до сих пор частых уязвимостей именно потому, что склеивать строки — соблазнительно просто.

Решение: параметризованные запросы

Правильный подход — никогда не вставлять ввод в текст запроса. Вместо этого в запросе ставят placeholder (заполнитель), а данные передают отдельно. База обрабатывает их строго как значение, а не как часть команды. Это называют параметризованными запросами или prepared statements.

// БЕЗОПАСНО: ? — заполнитель, login передаётся как данные
query = "SELECT * FROM users WHERE name = ?"
выполнить(query, [login])   // login никогда не станет командой

Теперь, что бы пользователь ни ввёл — хоть кавычки, хоть ключевые слова SQL, — это останется просто строкой-значением. Граница между кодом и данными восстановлена. Параметризация закрывает SQL-инъекции полностью, а не «уменьшает риск».

Безопасный запрос работает как обычно

Параметризованный запрос — это обычный SQL, просто значения отделены. Вот рабочий пример самой выборки (запустите в SQL-песочнице):

CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, role TEXT);
INSERT INTO users (name, role) VALUES ('alice', 'admin');
INSERT INTO users (name, role) VALUES ('bob', 'user');

-- Здесь 'alice' играет роль безопасно переданного параметра
SELECT id, name, role FROM users WHERE name = 'alice';

Запрос вернёт строку Алисы. В реальном коде значение 'alice' вы бы не вписывали в текст, а передавали отдельным параметром — но сам SELECT выглядит точно так же.

Дополнительные слои защиты

  • Валидация ввода. Ожидаете число — проверьте, что пришло число. Это не замена параметризации, а дополнение.
  • Наименьшие привилегии. Учётная запись приложения к БД не должна иметь прав удалять таблицы, если ей это не нужно.
  • ORM и query builder. Большинство современных библиотек параметризуют запросы автоматически — но только если не подсовывать им «сырые» строки.

Итог

  • SQL-инъекция рождается из склейки запроса со строкой пользователя.
  • Параметризованные запросы передают данные отдельно от текста команды и закрывают проблему полностью.
  • Валидация и наименьшие привилегии — дополнительные слои, а не замена параметризации.
  • ORM помогает, но только пока вы не вставляете сырые строки вручную.
Проверьте себя
1. Что является корневой причиной SQL-инъекции?
AИспользование базы данных вообще
BСклейка текста запроса с непроверенным вводом пользователя
CСлишком длинные пароли
DШифрование трафика
2. Почему параметризованные запросы защищают от инъекции?
AОни шифруют данные в базе
BДанные передаются отдельно от текста запроса и трактуются только как значение
CОни ускоряют выполнение запроса
DОни скрывают имя таблицы
3. Достаточно ли одной валидации ввода вместо параметризации?
AДа, валидация полностью заменяет параметризацию
BНет, валидация — дополнительный слой, а основой защиты остаётся параметризация
CДа, если ввод короткий
DПараметризация вообще не нужна
4. Какой принцип ограничивает ущерб при успешной инъекции?
AХранение паролей в открытом виде
BНаименьшие привилегии учётной записи приложения к БД
CОтключение логирования
DДлинные имена таблиц
Поддержать проект