Карта / SQL / SQL-инъекции и защита

SQL-инъекции и защита

SQL-инъекция — атака, при которой пользовательский ввод “ломает” структуру SQL-запроса и заставляет базу выполнить то, что не планировалось.

// ❌ опасно: текст пользователя вставляется прямо в запрос
String query = "SELECT * FROM users WHERE name = '" + userInput + "'";

Если userInput равен ' OR '1'='1, итоговый запрос станет:

SELECT * FROM users WHERE name = '' OR '1'='1'

'1'='1' всегда истина — запрос вернёт всех пользователей, а не нужного одного. Более опасные варианты могут удалить данные или получить доступ к чужим записям.

Защита — PreparedStatement, где данные передаются отдельно от текста запроса, не как часть строки:

PreparedStatement stmt = connection.prepareStatement("SELECT * FROM users WHERE name = ?");
stmt.setString(1, userInput);   // безопасно — userInput не может изменить структуру запроса

Подробнее про PreparedStatement — в теме «PreparedStatement».

Копнуть глубже

Почему PreparedStatement защищает. База получает текст запроса (SELECT * FROM users WHERE name = ?) и значение параметра раздельно, на разных этапах. Значение никогда не интерпретируется как часть SQL-кода, какие бы кавычки и спецсимволы в нём ни были — оно всегда трактуется только как данные.

ORM (Hibernate/JPA) защищает от инъекций по умолчанию — он сам использует PreparedStatement под капотом для подстановки параметров. Уязвимость может вернуться, только если вручную собирать JPQL/SQL строку через конкатенацию (createQuery("... WHERE name = '" + name + "'")) — этого делать никогда не стоит, даже с ORM.

Это пункт #3 в OWASP Top 10 (см. тему «OWASP Top 10») — одна из самых известных и до сих пор встречающихся уязвимостей веб-приложений.

🎤 Закрыл тему, если можешь объяснить:
• как работает SQL-инъекция на примере конкатенации строки;
• почему PreparedStatement защищает и почему ORM не спасает от ручной конкатенации (если дошёл до 2-го слоя).