Чем PreparedStatement безопаснее обычного Statement?
- Как защититься от SQL-инъекций в Java?
- Чем PreparedStatement быстрее Statement?
PreparedStatement передаёт текст запроса и значения раздельно: значение никогда не разбирается как SQL — инъекция невозможна. Statement склеивает строку и выполняет как есть.
- Плейсхолдеры
?иsetString,setLong - Повторное выполнение может использовать подготовленный план
- Батчи:
addBatchиexecuteBatch
А ещё Имена таблиц и колонок параметром не передать — только белый список
Подробно
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, поэтому не становятся кодом
- ✗Он экранирует кавычки в склеенной строке запроса, и инъекция в ней уже невозможна
Что спросят дальше
- Как закрывать JDBC resources?
- Что такое connection pool?
Разбор темы
- JDBC и безопасные запросы
Connection/Statement/ResultSet, PreparedStatement против инъекций, ручные транзакции, ресурсы и пул соединений.
Связанные вопросы
Разбор собеседования с sotto
После собеседования sotto разбирает разговор: оценка, каждый вопрос — хорошо, частично или слабо, чего не хватило в ответе и над чем поработать к следующему разу. Первые 30 минут бесплатно.