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.