Можно ли хранить только хэш пароля
Что проверяют
Знаете ли вы, что хранить хэш не только можно, но и нужно, и понимаете ли, чем хэш для паролей отличается от хэша вообще. Для Java ждут ещё и практики: какой класс возьмёте и как потом смените алгоритм.
Как отвечать
Да, и хранить нужно ровно хэш. Обратимое шифрование не подходит: ключ лежит в том же контуре, и утечка базы вместе с конфигурацией отдаёт все пароли открытым текстом. Проверка при входе идёт сравнением хэшей, восстановление заменяется сбросом по ссылке из письма.
Ключевое уточнение про алгоритм. Пароль хэшируют медленной функцией с настраиваемой стоимостью: bcrypt, scrypt, Argon2, PBKDF2. Быстрые SHA-256 и тем более MD5 не годятся, потому что на видеокарте перебираются огромными скоростями. Соль обязательна, но защищает она от заранее посчитанных таблиц, а не от перебора; у bcrypt она лежит внутри строки хэша, поэтому отдельная колонка не нужна.
В Spring Security рабочий путь это PasswordEncoder, а конкретно DelegatingPasswordEncoder из фабрики по умолчанию. Он пишет хэш с пометкой алгоритма и проверяет через matches, поэтому старые записи продолжают работать, а новые пишутся новым алгоритмом. Стоимость подбирают замером на своём железе: повышают параметр, пока проверка не станет заметной, но терпимой при входе.
Что ещё уместно назвать: секрет из конфигурации, добавляемый к паролю, и отметку о времени смены. Сам пароль в памяти держат недолго, в идеале в массиве символов с затиранием, хотя часть библиотек всё равно принимает строку.
Где спотыкаются
Считают, что соль решает вопрос. Соль с быстрым алгоритмом оставляет перебор по одному пользователю делом минут, потому что стоимость одной попытки осталась ничтожной. Спрашивают именно про адаптивную функцию.
Не закладывают смену алгоритма. Хэши записаны без версии, и через пару лет мигрировать можно только сбросом паролей всем сразу. Пометка алгоритма прямо в строке снимает проблему заранее.
Сравнивают хэши самодельным кодом. Ручное вычисление и сравнение строк обходит библиотечную проверку и отдаёт информацию по времени сравнения; в Java для побайтового сравнения есть MessageDigest.isEqual, а для паролей правильнее не выходить за пределы matches.