Перейти к основному содержимому

Хеширование пароля в Java

· 5 мин. чтения

1. Обзор

В этом уроке мы обсудим важность хеширования паролей.

Мы кратко рассмотрим, что это такое, почему это важно, а также некоторые безопасные и небезопасные способы сделать это в Java.

2. Что такое хеширование?

Хеширование — это процесс генерации строки или хэша из данного сообщения с использованием математической функции, известной как криптографическая хеш-функция .

Хотя существует несколько хеш-функций, те из них, которые предназначены для хэширования паролей, должны обладать четырьмя основными свойствами для обеспечения безопасности:

  1. Он должен быть детерминированным : одно и то же сообщение, обработанное одной и той же хеш-функцией, всегда должно давать один и тот же хэш .
  2. Это необратимо : нецелесообразно генерировать сообщение из его хеша .
  3. У него высокая энтропия : небольшое изменение в сообщении должно привести к совершенно другому хэшу .
  4. И он противостоит коллизиям : два разных сообщения не должны создавать один и тот же хэш .

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

Кроме того, функции хеширования паролей должны быть медленными . Быстрый алгоритм поможет атакам грубой силы , в которых хакер попытается угадать пароль, хэшируя и сравнивая миллиарды ( или триллионы ) потенциальных паролей в секунду.

Некоторые отличные хэш-функции, которые соответствуют всем этим критериям, — это 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 дает нам PBKDF2
  • BCryptPasswordEncoder дает нам BCrypt, и
  • SCryptPasswordEncoder дает нам SCrypt

Все кодировщики паролей для PBKDF2, BCrypt и SCrypt поддерживают настройку желаемой надежности хэша пароля.

Мы можем использовать эти кодировщики напрямую, даже без приложения на основе Spring Security. Или, если мы защищаем наш сайт с помощью Spring Security, мы можем настроить желаемый кодировщик паролей через его DSL или через внедрение зависимостей .

И, в отличие от наших примеров выше, эти алгоритмы шифрования будут генерировать соль для нас внутри . Алгоритм сохраняет соль в выходном хеше для последующего использования при проверке пароля.

7. Заключение

Итак, мы глубоко погрузились в хеширование паролей; изучение концепции и ее использования.

И мы рассмотрели некоторые исторические хеш-функции, а также некоторые реализованные в настоящее время, прежде чем кодировать их на Java.

Наконец, мы увидели, что Spring Security поставляется со своими классами шифрования паролей, реализующими множество различных хэш-функций.

Как всегда, код доступен на GitHub.