1. Обзор
В этом уроке мы обсудим важность хеширования паролей.
Мы кратко рассмотрим, что это такое, почему это важно, а также некоторые безопасные и небезопасные способы сделать это в Java.
2. Что такое хеширование?
Хеширование — это процесс генерации строки или хэша
из данного сообщения
с использованием математической функции, известной как криптографическая хеш-функция
.
Хотя существует несколько хеш-функций, те из них, которые предназначены для хэширования паролей, должны обладать четырьмя основными свойствами для обеспечения безопасности:
- Он должен быть
детерминированным
: одно и то же сообщение, обработанное одной и той же хеш-функцией,всегда
должно давать один и тот жехэш .
- Это
необратимо
: нецелесообразно генерироватьсообщение
из егохеша .
- У него высокая
энтропия
: небольшое изменение всообщении
должно привести к совершенно другомухэшу .
- И он противостоит
коллизиям
: два разныхсообщения
не должны создавать один и тот жехэш .
Хеш-функция, обладающая всеми четырьмя свойствами, является хорошим кандидатом для хеширования паролей, поскольку вместе они значительно увеличивают сложность обратного проектирования пароля из хэша.
Кроме того, функции хеширования паролей должны быть медленными . Быстрый алгоритм поможет атакам грубой силы
, в которых хакер попытается угадать пароль, хэшируя и сравнивая миллиарды ( или триллионы ) потенциальных паролей в секунду.
Некоторые отличные хэш-функции, которые соответствуют всем этим критериям, — это PBKDF2,
BCrypt
и SCrypt.
Но сначала давайте взглянем на некоторые старые алгоритмы и почему они больше не рекомендуются. ** **
``
3. Не рекомендуется: MD5.
Наша первая хеш-функция — это алгоритм дайджеста сообщений MD5, разработанный еще в 1992 году.
MessageDigest
в Java упрощает вычисление и может быть полезен в других обстоятельствах.
Однако за последние несколько лет было обнаружено, что MD5 не соответствует четвертому свойству хеширования паролей , поскольку с вычислительной точки зрения стало легко генерировать коллизии. В довершение всего, MD5 является быстрым алгоритмом и поэтому бесполезен против атак грубой силы.
Из-за этого MD5 не рекомендуется.
4. Не рекомендуется: SHA-512.
Далее мы рассмотрим SHA-512, который является частью семейства алгоритмов безопасного хеширования, семейства, которое началось с SHA-0 еще в 1993 году.
4.1. Почему SHA-512?
По мере увеличения мощности компьютеров и обнаружения новых уязвимостей исследователи создают новые версии SHA. Более новые версии имеют прогрессивно большую длину, или иногда исследователи публикуют новую версию базового алгоритма.
SHA-512 представляет собой самый длинный ключ в третьем поколении алгоритма.
Хотя в настоящее время существуют более безопасные версии SHA , SHA-512 является самой надежной из реализованных в Java .
4.2. Реализация на Java
Теперь давайте посмотрим на реализацию алгоритма хеширования SHA-512 в Java.
Во-первых, мы должны понять концепцию соли
. Проще говоря, это случайная последовательность, которая генерируется для каждого нового хэша .
Вводя эту случайность, мы увеличиваем энтропию
хэша и защищаем нашу базу данных от предварительно скомпилированных списков хэшей, известных как радужные таблицы
.
Тогда наша новая хэш-функция становится примерно такой:
salt <- generate-salt;
hash <- salt + ':' + sha512(salt + password)
4.3. Создание соли
Чтобы ввести соль, мы будем использовать класс SecureRandom
из java.security
:
SecureRandom random = new SecureRandom();
byte[] salt = new byte[16];
random.nextBytes(salt);
Затем мы будем использовать класс MessageDigest
для настройки хеш-функции SHA-512
с нашей солью:
MessageDigest md = MessageDigest.getInstance("SHA-512");
md.update(salt);
И с учетом этого мы теперь можем использовать метод дайджеста
для генерации нашего хешированного пароля:
byte[] hashedPassword = md.digest(passwordToHash.getBytes(StandardCharsets.UTF_8));
4.4. Почему это не рекомендуется?
При использовании с солью SHA-512 по-прежнему является приемлемым вариантом , но существуют более сильные и медленные варианты .
Кроме того, остальные параметры, которые мы рассмотрим, имеют важную особенность: настраиваемую силу.
5. PBKDF2, BCrypt и SCrypt
PBKDF2, BCrypt и SCrypt — три рекомендуемых алгоритма.
5.1. Почему их рекомендуют?
Каждый из них медленный, и у каждого есть блестящая особенность настраиваемой силы.
Это означает, что по мере увеличения мощности компьютеров мы можем замедлить алгоритм, изменив входные данные.
5.2. Реализация PBKDF2 на Java
Так вот , соли — это фундаментальный принцип хеширования паролей , поэтому нам нужен он и для PBKDF2:
SecureRandom random = new SecureRandom();
byte[] salt = new byte[16];
random.nextBytes(salt);
Далее мы создадим PBEKeySpec
и SecretKeyFactory
, экземпляры которых мы создадим с помощью алгоритма PBKDF2WithHmacSHA1
:
KeySpec spec = new PBEKeySpec(password.toCharArray(), salt, 65536, 128);
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
Третий параметр ( 65536
) фактически является параметром прочности. Он указывает, сколько итераций выполняет этот алгоритм, увеличивая время, необходимое для создания хэша.
Наконец, мы можем использовать нашу SecretKeyFactory
для генерации хеша:
byte[] hash = factory.generateSecret(spec).getEncoded();
5.3. Реализация BCrypt и SCrypt в Java
Итак, получается, что поддержка BCrypt и SCrypt еще не поставляется с Java , хотя некоторые библиотеки Java их поддерживают.
Одной из таких библиотек является Spring Security.
6. Хеширование паролей с помощью Spring Security
Хотя Java изначально поддерживает алгоритмы хэширования PBKDF2 и SHA, она не поддерживает алгоритмы BCrypt и SCrypt.
К счастью для нас, Spring Security поставляется с поддержкой всех этих рекомендуемых алгоритмов через интерфейс PasswordEncoder :
Pbkdf2PasswordEncoder
дает нам PBKDF2BCryptPasswordEncoder
дает нам BCrypt, иSCryptPasswordEncoder
дает нам SCrypt
Все кодировщики паролей для PBKDF2, BCrypt и SCrypt поддерживают настройку желаемой надежности хэша пароля.
Мы можем использовать эти кодировщики напрямую, даже без приложения на основе Spring Security. Или, если мы защищаем наш сайт с помощью Spring Security, мы можем настроить желаемый кодировщик паролей через его DSL или через внедрение зависимостей .
И, в отличие от наших примеров выше, эти алгоритмы шифрования будут генерировать соль для нас внутри . Алгоритм сохраняет соль в выходном хеше для последующего использования при проверке пароля.
7. Заключение
Итак, мы глубоко погрузились в хеширование паролей; изучение концепции и ее использования.
И мы рассмотрели некоторые исторические хеш-функции, а также некоторые реализованные в настоящее время, прежде чем кодировать их на Java.
Наконец, мы увидели, что Spring Security поставляется со своими классами шифрования паролей, реализующими множество различных хэш-функций.
Как всегда, код доступен на GitHub.