Защита токенов с помощью AES-CBC: просто о важном в Java
Салимжанов Р.Д.
Введение: Зачем это нужно?
Токены — это цифровые ключи, которые подтверждают вашу личность в приложениях. Как и настоящие ключи, их нужно защищать. Представьте, что злоумышленник украл ваш токен — он получит полный доступ к аккаунту.
AES (Advanced Encryption Standard) — это симметричный алгоритм шифрования, который использует один и тот же ключ для шифрования и дешифрования данных. Он работает с блоками фиксированного размера (128 бит) и поддерживает ключи длиной 128, 192 или 256 бит.
CBC (Cipher Block Chaining) — это режим работы AES, который добавляет дополнительную защиту. Каждый блок данных перед шифрованием "смешивается" с результатом шифрования предыдущего блока. Это делает шифр более устойчивым к атакам.
Зачем шифровать токены?
- Защита от перехвата в куках
- Предотвращение подделки токенов
- Соблюдение стандартов безопасности (например, GDPR)
Внедряем шифрование в приложение
Рассмотрим, как интегрировать шифрование токенов с использованием AES в режиме CBC в ваше Spring Boot приложение.
1. Создаем "шифровальщик" (AesEncryptionUtil)
Этот класс — наш цифровой сейф:
Сначала создадим класс AesEncryptionUtil, который будет содержать методы для шифрования и дешифрования данных с использованием AES/CBC/PKCS5Padding.
Представь, что у тебя есть секретное письмо, которое нужно спрятать в сейф.
Класс AesEncryptionUtil — это твой робот-шифровальщик, который умеет делать две вещи:
Запираем письмо в сейф (шифрование)
Шаг 1: Проверяем ключ
Робот говорит: «Дайте мне секретный ключ длиной минимум 16 символов!»
Вместо того чтобы передавать ключ в метод, получаем его из переменной окружения AES_SECRET_KEY
Про файл .env я рассказывал в одной из своих статей
Шаг 2: Генерируем «соль» (IV)
Робот берет 16 случайных кубиков (IV — Initialization Vector).
Это как посыпать письмо солью перед тем, как его запечатать — так сейф будет уникальным.
Шаг 3: Создаем замок (ключ)
Робот превращает ваш ключ в специальный «замок» для сейфа.
Шаг 4: Шифруем
Письмо (токен) нарезается на кусочки, смешивается с «солью» и запирается в сейф.
Робот использует алгоритм AES-CBC — это как сложный пазл, где каждый кусочек зависит от предыдущего.
Шаг 5: Упаковываем
Сейф (IV + зашифрованные данные) кодируется в Base64 — это как перевод текста на язык роботов, чтобы его можно было безопасно передать.
Для расшифровки процесса мы выполняем обратные шаги: декодируем Base64, извлекаем IV и зашифрованные данные, и затем расшифровываем сообщение с использованием того же секретного ключа.
2. Настраиваем безопасность (SecurityConfig)
Представьте, что у вас есть здание с разными зонами доступа:
- Главный вход открыт для всех.
- Вход в комнату администратора (/admin/**) только для пользователей с ролью ROLE_ADMIN.
- Вход в пользовательскую зону (/user/**) только для пользователей с ROLE_USER.
- Везде нужны пропуска (аутентификация).
Это и делает SecurityFilterChain — управляет доступом пользователей к ресурсам, проверяя их права.
Шаг 1: Получаем токен
После успешного входа Spring передает нам токен аутентификации:
String token = authentication.getCredentials().toString();
Шаг 2: Получаем секретный ключ
Шифровать будем с помощью специального ключа. Мы берем ключ из переменной окружения AES_SECRET_KEY.
String secretKey = System.getenv("AES_SECRET_KEY");
if (secretKey == null) {
System.out.println("AES_SECRET_KEY is not set in environment variables!");
}
Шаг 3: Шифруем токен
Теперь даже если кто-то украдет cookie, он не сможет расшифровать токен без секретного ключа.
String encryptedToken = AesEncryptionUtil.encrypt(token, secretKey);
System.out.println("Encrypted token: " + encryptedToken);
Шаг 4: Сохраняем в cookie
Мы создаем cookie с зашифрованным токеном, которая:
1)HttpOnly—браузер не может прочитать её через JavaScript (защита от XSS).
2)Secure—отправляется только через HTTPS.
3)MaxAge—живёт 7 дней.
Cookie cookie = new Cookie("encrypted_token", encryptedToken);
cookie.setHttpOnly(true);
cookie.setSecure(true); // Только HTTPS
cookie.setPath("/");
cookie.setMaxAge(7 * 24 * 60 * 60); // 7 дней
response.addCookie(cookie);
System.out.println("Encrypted token set in cookie.");
После этого происходит редирект на главную страницу:
response.sendRedirect("/");
Проверка
Так запустим и проверим, не зря же кучу сообщений в логи выводятся.
Давайте разберем каждую строку:
Token received:
Это сообщение указывает на то, что токен был получен. Однако сам токен не отображается в логах, возможно, по соображениям безопасности.
AES_SECRET_KEY found: 1234567890abcdef
Здесь сообщается, что был найден секретный ключ для шифрования AES. В данном случае ключ имеет значение 1234567890abcdef. Обратите внимание, что использование простых и предсказуемых ключей (как в этом примере) не является безопасной практикой.
Encrypting data with AES key: 1234567890abcdef
Эта строка указывает на то, что данные (в данном случае токен) шифруются с использованием найденного ключа AES.
Encryption successful. Encrypted data size: 32
Сообщение подтверждает, что шифрование прошло успешно, и указывает размер зашифрованных данных (в байтах). В данном случае размер составляет 32 байта.
Encrypted token: 4bJ7ok9Vo+/NUwPoxqfBLlev33ne3Gxulfib9szmL4M=
Это зашифрованный токен, представленный в виде строки, закодированной в Base64. Эта строка может быть использована для передачи или хранения зашифрованного токена.
Encrypted token set in cookie.
В этой строке сообщается, что зашифрованный токен был установлен в cookie. Это означает, что зашифрованные данные будут храниться в cookie браузера пользователя, что позволяет использовать их для аутентификации или авторизации при последующих запросах.
Вывод: Это действительно необходимо?
Почему стоит потерпеть сложности?
- Защита от атак: Даже при утечке куки злоумышленник не получит настоящий токен
- Соблюдение стандартов: Требуется по PCI DSS, HIPAA и другим регуляторам
- Доверие пользователей: Ваши клиенты уверены в безопасности
Как замок на двери — шифрование добавляет один шаг, но защищает от самых серьезных угроз. В мире, где утечки данных случаются ежедневно, это необходимый минимум для любого серьезного приложения.
СПИСОК ИСПОЛЬЗОВАННЫХ ИСТОЧНИКОВ
1) Java AES Encryption and Decryption// [электронный ресурс]. URL: https://www.baeldung.com/java-aes-encryption-decryption (дата обращения 08.02.2025).
2) AES шифрование и Android клиент // [электронный ресурс]. URL: https://habr.com/ru/companies/rambler_and_co/articles/279835/ (дата обращения 09.02.2025).
3) аутентификация при помощи Spring Boot // [электронный ресурс]. URL: https://habr.com/ru/articles/784508/ (дата обращения 08.02.2025).