A03: Injection и защита от SQL-инъекций

Инъекция возникает, когда данные пользователя интерпретируются как команда — и параметризация это полностью предотвращает.

Injection — внедрение: непроверенные данные пользователя попадают в интерпретатор (SQL, командную оболочку, шаблон) и выполняются как часть команды, а не как данные.

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

Как возникает уязвимость

Опасный паттерн — конкатенация строк. Когда логин подставляется в текст запроса напрямую, специальные символы в нём меняют структуру запроса.

// ОПАСНО: запрос склеивается из строки и ввода
zapros = "SELECT * FROM users WHERE login = '" + vvod + "'"
// Если ввод содержит кавычку, структура запроса ломается

Мы намеренно не приводим боевых строк-эксплойтов: важна суть — пользовательский текст не должен попадать в код запроса напрямую.

Защита: параметризованные запросы

Параметризованный запрос (prepared statement) — главное и почти полное решение. Структура запроса фиксируется заранее, а данные передаются отдельно, как параметры. Драйвер базы данных гарантирует, что параметр трактуется только как значение, что бы в нём ни было.

-- Правильно: структура и данные разделены
SELECT * FROM users WHERE login = ?;
-- значение login передаётся отдельным параметром драйвера

Можно потренироваться с корректным SQL в песочнице ниже:

CREATE TABLE users (id INTEGER PRIMARY KEY AUTOINCREMENT, login TEXT);
INSERT INTO users (login) VALUES ('anna'), ('boris'), ('vera');
SELECT * FROM users WHERE login = 'anna';

Другие инъекции

Тот же принцип — для команд ОС (не склеивайте shell-команду из ввода, используйте безопасные API с массивом аргументов), для NoSQL, LDAP и шаблонизаторов. Везде идея одна: разделять код и данные.

Как защищаться

  • Всегда используйте параметризованные запросы или ORM, который их применяет.
  • Дополнительно валидируйте ввод по белому списку (например, число там, где ожидается число).
  • Экранируйте вывод по контексту (но это вторая линия, не замена параметризации).
  • Давайте приложению минимальные права в БД (см. урок про least privilege).

Частые ошибки разработчиков

  • Считают, что «экранировать кавычки вручную» достаточно — параметризация надёжнее и проще.
  • Параметризуют значения, но имена таблиц/колонок всё равно склеивают из ввода (их валидируют по белому списку).
  • Полагаются на проверку ввода на фронтенде, забывая, что запрос можно отправить напрямую.

Итог

  • Инъекция — это смешение кода и данных в интерпретаторе.
  • Параметризованные запросы разделяют их и почти полностью закрывают SQLi.
  • Принцип «разделяй код и данные» работает и для команд ОС, и для NoSQL.
Проверьте себя
1. Что является главной защитой от SQL-инъекций?
AСкрыть форму ввода
BПараметризованные запросы (prepared statements)
CЗапретить кавычки на фронтенде
DИспользовать HTTPS
2. В чём корневая причина любой инъекции?
AСлабый пароль
BДанные пользователя интерпретируются как часть команды
CОтсутствие HTTPS
DБольшая нагрузка
3. Почему опасна конкатенация строк при построении SQL-запроса?
AОна медленнее
BСпециальные символы во вводе могут изменить структуру запроса
CОна занимает много памяти
DОна не работает в новых базах