Безопасность Java приложений: топ-10 уязвимостей для начинающих
Java остаётся одним из самых популярных языков для создания корпоративных и веб-приложений. Однако даже надёжная платформа не гарантирует безопасность кода. По данным OWASP, большинство взломов происходят из-за ошибок разработчиков, а не из-за уязвимостей в JVM. В этой статье мы разберём топ-10 уязвимостей Java-приложений, которые должен знать каждый начинающий разработчик. Вы получите не только теоретические знания, но и практические примеры кода с объяснением, как избежать опасных ошибок.
1. SQL-инъекции (SQL Injection)
Самая распространённая угроза. Злоумышленник может выполнить произвольный SQL-запрос, если вы конкатенируете строки напрямую.
Плохой пример (уязвимый код):
String query = "SELECT * FROM users WHERE username = '" + userName + "' AND pass = '" + password + "'";
Statement stmt = connection.createStatement();
ResultSet rs = stmt.executeQuery(query);
Решение: используйте PreparedStatement с параметризованными запросами.
String query = "SELECT * FROM users WHERE username = ? AND pass = ?";
PreparedStatement pstmt = connection.prepareStatement(query);
pstmt.setString(1, userName);
pstmt.setString(2, password);
ResultSet rs = pstmt.executeQuery();
2. Межсайтовый скриптинг (XSS)
Если вы выводите пользовательские данные в HTML без экранирования, злоумышленник может внедрить JavaScript-код.
Уязвимый код (JSP):
<%= request.getParameter("message") %>
Защита: экранируйте вывод с помощью библиотек (например, OWASP Java Encoder) или используйте шаблонизаторы с автоэкранированием (Thymeleaf, JSP с JSTL).
<c:out value="${message}" />
3. Небезопасная десериализация
Java-десериализация может привести к удалённому выполнению кода (RCE), если злоумышленник передаёт специально сформированный поток байтов.
Плохая практика:
ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
Object obj = ois.readObject(); // что угодно может быть восстановлено
Рекомендации:
- Никогда не десериализуйте данные из ненадёжных источников.
- Используйте белые списки классов (look-ahead object input stream).
- Рассмотрите альтернативы (JSON, Protobuf).
4. Внедрение команд ОС (Command Injection)
Если ваше приложение вызывает системные команды с пользовательскими данными, возможна инъекция.
Опасный код:
Runtime.getRuntime().exec("ping " + userInput); // userInput = 127.0.0.1; rm -rf /
Защита: избегайте вызова shell-команд, используйте встроенные Java-библиотеки. Если вызов неизбежен — валидируйте входные данные строгим белым списком.
5. Недостатки аутентификации и управления сессиями
Слабая сессионная защита — частая проблема. Например, использование предсказуемых идентификаторов сессий или хранение паролей в открытом виде.
- Генерируйте сессионные ID с помощью
SecureRandom. - Используйте современные хеши (bcrypt, scrypt, Argon2) для паролей.
- Устанавливайте тайм-ауты сессий и флаги
HttpOnly/Secureдля cookies.
6. Небезопасная конфигурация (Security Misconfiguration)
Оставленные debug-режимы, стандартные пароли, открытые порты — всё это даёт злоумышленнику информацию о системе.
Что проверить в Spring Boot:
- Отключите Actuator endpoints в продакшене или защитите их.
- Не используйте
spring.jpa.show-sql=trueв production. - Удалите стандартные заголовки сервера.
7. XML External Entities (XXE)
Если вы парсите XML-документы, злоумышленник может внедрить внешнюю сущность, которая прочитает файлы на сервере или вызовет DoS.
Уязвимый парсер:
DocumentBuilder db = DocumentBuilderFactory.newInstance().newDocumentBuilder();
Document doc = db.parse(inputStream); // XXE возможна
Защита: отключите внешние сущности и DTD:
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
dbf.setFeature("http://xml.org/sax/features/external-general-entities", false);
8. Утечка конфиденциальных данных в логах
Логирование паролей, токенов или кредитных карт — грубая ошибка.
Пример ошибки:
log.info("User login: " + username + " password: " + password);
Решение: никогда не логируйте секреты. Используйте маскирование или фильтры в Logback/Log4j.
9. Недостаточная проверка доступа (Broken Access Control)
Пользователь может получить доступ к данным другого пользователя, если не проверять права на уровне объектов.
Уязвимость: если в запросе передаётся userId=123, а сервер не проверяет, что текущий пользователь — владелец этого ID.
Исправление: всегда проверяйте, что аутентифицированный пользователь имеет право на запрашиваемый ресурс (например, через Spring Security @PreAuthorize).
10. Использование компонентов с известными уязвимостями
Библиотеки с уязвимостями (Log4Shell, старые версии Jackson) — прямой путь к компрометации.