Java-безопасность: Как защитить код от SQL-инъекций и XSS-атак

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Как не дать хакерам взломать ваш Java-код: SQL-инъекции и XSS под микроскопом

Представьте: вы написали идеальное веб-приложение на Java. Красивый интерфейс, быстрая логика, чистая архитектура. Вы выкладываете его на сервер, завариваете чай и... через час получаете сообщение, что база данных удалена, а на главной странице красуется чей-то дерзкий ASCII-арт. Знакомая история? К сожалению, для многих разработчиков — да.

Две самые распространённые и опасные уязвимости веб-приложений — SQL-инъекции и межсайтовый скриптинг (XSS) — ежегодно лишают сна администраторов тысяч сайтов. И Java, несмотря на свою строгость, не является исключением. Но есть и хорошая новость: защититься от них реально. И даже не очень сложно.

В этой статье мы разберём, как именно злоумышленники атакуют Java-приложения, и главное — как поставить надёжный блокпост с помощью проверенных инструментов и правильных привычек кодирования. Пристегните ремни: будет много кода, лайфхаков и минимум воды.

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

Что это такое и почему это работает?

SQL-инъекция — это техника атаки, при которой злоумышленник «впрыскивает» вредоносный SQL-код в запрос через пользовательский ввод. Если приложение бездумно склеивает строки, атакующий может получить доступ к чужим данным, удалить таблицы или даже захватить управление над сервером базы данных.

Классический пример плохого кода на Java:

String userInput = request.getParameter("username");
String query = "SELECT * FROM users WHERE username = '" + userInput + "'";
Statement stmt = connection.createStatement();
ResultSet rs = stmt.executeQuery(query);

Что происходит, если пользователь вводит: admin' OR '1'='1? Запрос превращается в:

SELECT * FROM users WHERE username = 'admin' OR '1'='1'

Условие '1'='1' всегда истинно. Поздравляю: злоумышленник только что вошёл в систему как администратор, даже не зная пароля. А если он введёт '; DROP TABLE users; -- — пиши пропало.

Как защититься: PreparedStatement — ваш щит

Правило номер один в безопасности Java-приложений: никогда не конкатенируйте пользовательские данные с SQL-запросами. Используйте параметризованные запросы через PreparedStatement.

Вот как должен выглядеть безопасный код:

String userInput = request.getParameter("username");
String query = "SELECT * FROM users WHERE username = ?";
PreparedStatement pstmt = connection.prepareStatement(query);
pstmt.setString(1, userInput);
ResultSet rs = pstmt.executeQuery();

Что здесь произошло? База данных получает запрос и данные отдельно. Даже если userInput содержит зловредный SQL-код, он будет воспринят как обычная строка, а не как команда. PreparedStatement автоматически экранирует спецсимволы. Это первая и самая важная линия обороны.

Дополнительные меры: хранимые процедуры и валидация

Помимо PreparedStatement, стоит использовать:

  • Хранимые процедуры — вызов процедуры через CallableStatement также отделяет код от данных.
  • Белые списки — если нужно подставить имя таблицы или столбца (что бывает редко), никогда не берите его из ввода пользователя. Используйте enum или строго заданные константы.
  • Валидация ввода — проверяйте, что email действительно похож на email, а число — это число. Но помните: валидация — это дополнительный слой, а не замена PreparedStatement.

Пример безопасной работы с динамическими именами таблиц:

public enum TableName {
    USERS, PRODUCTS, ORDERS
}

public void queryTable(TableName table) {
    String query = "SELECT * FROM " + table.name().toLowerCase();
    // Используйте Statement только если имя таблицы строго из enum!
    Statement stmt = connection.createStatement();
    ResultSet rs = stmt.executeQuery(query);
}

Этот подход исключает возможность инъекции, так как значение берётся из ограниченного набора констант.

XSS-атаки: когда ваш сайт атакует сам себя

Тихая угроза на каждой странице

Межсайтовый скриптинг (Cross-Site Scripting, XSS) — это уязвимость, при которой злоумышленник внедряет в веб-страницу вредоносный JavaScript-код. Этот код выполняется в браузере жертвы, крадёт куки, перенаправляет на фишинговые сайты или портит интерфейс.

Различают три основных типа XSS:

  • Отражённый (Reflected) — вредоносный код передаётся через URL или форму и сразу отображается на странице.
  • Хранимый (Stored) — код сохраняется на сервере (в БД, файле) и отображается всем посетителям (например, в комментариях).
  • DOM-based — уязвимость на стороне клиента, когда JavaScript манипулирует DOM-деревом на основе непроверенных данных.

Пример уязвимого кода на Java (JSP или сервлет):

String comment = request.getParameter("comment");
out.println("<p>Ваш комментарий: " + comment + "</p>");

Если пользователь отправит: <script>alert('XSS')</script>, то каждый, кто откроет эту страницу, увидит всплывающее окно. А если вместо alert будет код, отправляющий куки на сервер злоумышленника? Последствия могут быть катастрофическими.

Экранирование вывода: главный принцип защиты от XSS

Основное правило защиты от XSS в Java: никогда не доверяйте данным, которые пришли от пользователя. Любой вывод, содержащий пользовательские данные, должен быть экранирован.

Используйте библиотеки для экранирования HTML. Самые популярные:

  • OWASP Java HTML Sanitizer — мощный инструмент для очистки HTML.
  • Apache Commons Text — содержит утилиты для экранирования.
  • Spring Framework — встроенные механизмы в JSP (JSTL) и Thymeleaf.

Пример с Apache Commons Text:

import org.apache.commons.text.StringEscapeUtils;

String userInput = request.getParameter("comment");
String safeComment = StringEscapeUtils.escapeHtml4(userInput);
out.println("<p>Ваш                

Блоги

Рекомендаций по книгам