Получить доступ

Чем PreparedStatement безопаснее обычного Statement?

JuniorJDBCSQL
Ещё спрашивают так:
  • Как защититься от SQL-инъекций в Java?
  • Чем PreparedStatement быстрее Statement?
Коротко

PreparedStatement передаёт текст запроса и значения раздельно: значение никогда не разбирается как SQL — инъекция невозможна. Statement склеивает строку и выполняет как есть.

  • Плейсхолдеры ? и setString, setLong
  • Повторное выполнение может использовать подготовленный план
  • Батчи: addBatch и executeBatch

А ещё Имена таблиц и колонок параметром не передать — только белый список

Так подсказку видит пользователь sotto на живом собеседовании — за пару секунд, пока вы думаете вслух.

Подробно

Statement выполняет SQL-строку, собранную приложением. Если в неё попадает ввод пользователя ("... WHERE login = '" + login + "'"), ввод ' OR '1'='1 меняет смысл запроса — SQL-инъекция.

PreparedStatement содержит запрос с плейсхолдерами ?, а значения передаются отдельно через setString, setLong и т. д. Драйвер и база обрабатывают их как данные, а не как часть SQL, сколько бы кавычек там ни было.

Дополнительные плюсы:

  • производительность — база может разобрать и спланировать запрос один раз и выполнять многократно (серверные prepared statements; в PostgreSQL драйвер переходит на них после нескольких выполнений — prepareThreshold);
  • типы — даты, числа, двоичные данные передаются корректно без ручного форматирования;
  • пакетная обработка — addBatch/executeBatch для массовых вставок.

Ограничения: параметром нельзя передать имя таблицы, колонки, направление сортировки — для них белый список допустимых значений. Список для IN (...) — нужное число плейсхолдеров или = ANY(?) с массивом в PostgreSQL.

В JPA и Spring Data параметры именованные (:id), но под капотом — тот же PreparedStatement; инъекции возвращаются при конкатенации строк в JPQL или нативных запросах.

Пример

try (PreparedStatement ps = conn.prepareStatement(
        "SELECT id, total FROM orders WHERE customer_id = ? AND status = ?")) {
    ps.setLong(1, customerId);
    ps.setString(2, status);
    try (ResultSet rs = ps.executeQuery()) { /* ... */ }
}

Что хотят услышать

  • Раздельная передача запроса и данных как защита от инъекций.
  • Пример уязвимой конкатенации.
  • Дополнительные плюсы: план, типы, батчи.
  • Что нельзя передать параметром.

Частые ошибки

  • Конкатенировать строку и передавать её в prepareStatement.
  • Вручную экранировать кавычки вместо параметров.

Проверьте себя

Один верный вариант. Подумайте, потом откройте ответ.

Показать варианты и ответ
  • ✓Параметры передаются отдельно от текста запроса, что защищает от SQL-инъекций
  • ✗Он шифрует текст SQL-запроса
  • ✗Он автоматически создаёт индексы под запрос
  • ✗Он запрещает любые запросы с условием WHERE

Что спросят дальше

  • Как закрывать JDBC resources?
  • Что такое connection pool?

Разбор темы

Связанные вопросы

Закрытая бета

Подсказки на собеседовании — в реальном времени

sotto слышит вопрос интервьюера и показывает короткий ответ и план, как в блоке «Коротко». Оставьте контакт — пришлём доступ и промокод.

  • Промокод на 2 часа — сверх пробных 30 минут
  • Программа для Windows — скачать сразу, войти — через Яндекс ID или VK ID
  • Прямая связь с разработчиком: что улучшить — сделаем