Kodokon kodokon.com

Безопасность: SQL-инъекции, привилегии, подготовленные запросы

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

11 мин · 3 вопросов

Открыть этот урок в Kodokon

SQL-инъекция возникает в тот момент, когда недоверенные данные склеиваются с текстом запроса. Атакующий подаёт значение, которое закрывает строку и перехватывает логику. Воспроизведём атаку на таблице users: прямая склейка со вводом ada' OR '1'='1 превращает точный фильтр во всегда истинное условие.

SQL
CREATE TABLE users (
  id INTEGER PRIMARY KEY,
  name TEXT NOT NULL,
  is_admin INTEGER NOT NULL DEFAULT 0
);

INSERT INTO users (id, name, is_admin)
VALUES (1, 'ada', 0), (2, 'root', 1);

-- Dangerous concatenation (NEVER do this)
-- input = ada' OR '1'='1
SELECT * FROM users
WHERE name = 'ada' OR '1'='1';
Запрос с инъекцией возвращает ВСЕ строки.

Полезная нагрузка срабатывает, потому что кавычка преждевременно закрывает литерал 'ada', а затем OR '1'='1' добавляет универсально истинное условие. Вариант этой атаки дописывает --, чтобы закомментировать остаток исходного запроса. Вывод: пока данные и код живут в одной строке, никакое ручное экранирование по-настоящему не надёжно.

SQL
-- Positional placeholder
SELECT * FROM users WHERE name = ?;

-- Named placeholder
SELECT * FROM users WHERE name = :name;
Две формы связываемых параметров в SQLite.

Лекарство - подготовленный запрос: драйвер сначала компилирует структуру запроса, а потом связывает значения отдельно. Связанные данные никогда не разбираются как SQL, они только сравниваются. Бонусом скомпилированная структура переиспользуется: связывая значения одно за другим, ты не перекомпилируешь план при каждом вызове.

SQL
-- The bound value stays data, not SQL
SELECT * FROM users
WHERE name = :name;
-- :name = ada' OR '1'='1
-- -> 0 rows: no user
--    is literally named that
Та же нагрузка, теперь безобидная.

Проверка знаний

Убедись, что запомнил ключевые моменты этого урока.

  1. Почему подготовленный запрос с параметрами предотвращает SQL-инъекцию?
    • Потому что он шифрует передаваемые значения
    • Потому что связанное значение считается данными и никогда не разбирается как SQL
    • Потому что он автоматически экранирует кавычки в строке
    • Потому что он ограничивает длину пользовательского ввода
  2. Нагрузка ' OR '1'='1 успешно внедряется, потому что она...
    • переполняет память сервера базы данных
    • закрывает строковый литерал и добавляет всегда истинное условие
    • удаляет таблицу, к которой обращается запрос
    • меняет привилегии текущего пользователя
  3. Можно ли связать имя таблицы через параметр ??
    • Да, плейсхолдеры принимают любой идентификатор
    • Нет: плейсхолдеры связывают только значения; идентификатор нужно проверять по белому списку
    • Да, но только в именованной форме :name
    • Да, при условии что таблица уже существует