Безопасная аутентификация в Java: как не провалить экзамен по безопасности

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

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

Начать курс

Введение: почему ваш пароль — это не просто строка

Представьте: вы написали крутое Java-приложение для онлайн-библиотеки. Пользователи регистрируются, сохраняют закладки, пишут рецензии. И вдруг — утечка базы данных. Все пароли в открытом виде. Катастрофа? Ещё какая! В 2024 году утечки данных происходят каждые 39 секунд, и 81% из них связаны со слабой аутентификацией. Если вы пишете на Java и хотите, чтобы ваш проект не попал в сводки киберновостей — эта статья для вас.

Мы разберём безопасную аутентификацию в Java приложениях от А до Я: от хэширования паролей до JWT и защиты от CSRF. Без скучной теории, только рабочий код и реальные практики. Поехали!

1. Хэширование паролей: первая линия обороны

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

Практика: bcrypt в действии

Добавьте зависимость в pom.xml (Maven):

<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-crypto</artifactId>
    <version>6.2.0</version>
</dependency>

Теперь хэшируем пароль при регистрации:

import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

public class PasswordService {
    private final BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();

    public String hashPassword(String rawPassword) {
        return encoder.encode(rawPassword); // генерирует соль автоматически
    }

    public boolean checkPassword(String rawPassword, String storedHash) {
        return encoder.matches(rawPassword, storedHash);
    }
}

Важно: bcrypt сам добавляет случайную соль, так что даже одинаковые пароли будут выглядеть по-разному. Стоимость (cost factor) по умолчанию — 10, но для критичных систем можно поднять до 12-14.

2. JWT (JSON Web Tokens): лёгкая и безопасная сессия

После регистрации пользователь должен как-то подтверждать свою личность. Куки? Можно, но JWT — современный стандарт для REST API. Токен содержит информацию о пользователе и подписан секретным ключом. Никаких сессий на сервере — всё в токене.

Создание JWT в Java

Подключите библиотеку io.jsonwebtoken:jjwt:

<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-api</artifactId>
    <version>0.12.3</version>
</dependency>

Генерация токена:

import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.security.Keys;
import javax.crypto.SecretKey;
import java.util.Date;

public class JwtUtil {
    private final SecretKey key = Keys.hmacShaKeyFor("секретный_ключ_длиной_не_менее_256_бит".getBytes());

    public String generateToken(String username) {
        return Jwts.builder()
                .subject(username)
                .issuedAt(new Date())
                .expiration(new Date(System.currentTimeMillis() + 3600000)) // 1 час
                .signWith(key)
                .compact();
    }

    public String extractUsername(String token) {
        return Jwts.parser()
                .verifyWith(key)
                .build()
                .parseSignedClaims(token)
                .getPayload()
                .getSubject();
    }
}

Совет: не храните секретный ключ в коде! Используйте переменные окружения или Vault. И никогда не кладите в JWT конфиденциальные данные (пароли, номера карт) — токен можно декодировать без подписи.

3. Защита от CSRF: когда злоумышленник действует от вашего имени

CSRF (Cross-Site Request Forgery) — это атака, при которой пользователь, будучи авторизованным, случайно выполняет действие на вашем сайте (например, переводит деньги). Java-фреймворки вроде Spring Security имеют встроенную защиту.

Spring Security и CSRF-токен

Если вы используете Spring Security, CSRF-защита включена по умолчанию для любых изменяющих запросов (POST, PUT, DELETE). Но для REST API с JWT её часто отключают — и это нормально, если токен не хранится в куках.

import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.SecurityFilterChain;

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    http
        .csrf(csrf -> csrf.disable()) // отключаем для REST API с JWT
        .authorizeHttpRequests(auth -> auth
            .requestMatchers("/api/auth/**").permitAll()
            .anyRequest().authenticated()
        );
    return http.build();
}

Когда CSRF обязателен: если вы используете сессии на основе кук (например, в монолитном веб-приложении). Тогда добавляйте токен в каждую форму:

<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" />

4. Хранение токенов: безопасность на стороне клиента

Вы сгенерировали JWT. Куда его сохранить на фронтенде? localStorage — плохая идея: если на сайт проникнет XSS-скрипт, токен украдут. httpOnly куки — лучше, но тогда нужна CSRF-защита.

Лучшая практика: httpOnly куки + Secure + SameSite

import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletResponse;

public void addTokenCookie(HttpServletResponse response, String token) {
    Cookie cookie = new Cookie("jwt", token);
    cookie.setHttpOnly(true);  // недоступен для JavaScript
    cookie.setSecure(true);    // только по HTTPS
    cookie.setPath("/");
    cookie.setMaxAge(3600);    // 1 час
    response.addCookie(cookie);
}

Почему это работает? XSS-атака не сможет прочитать куку, а Secure не даст перехватить её по HTTP. Для дополнительной защиты используйте атрибут SameSite=Strict.

5. Многофакторная аутентификация (MFA): двойной замок

Один пароль — хорошо, два — лучше. Если ваше приложение работает с финансами или персональными данными, добавьте второй фактор: TOTP (Time-based One-Time Password) через Google Authenticator или SMS.

Реализация TOTP в Java

Используйте библиотеку com.warrenstrange:googleauth:

<dependency>
    <groupId>com.warrenstrange</groupId>
    <artifactId                

Блоги

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