Как правильно хранить пароли в памяти и базе данных в Java
Что проверяют
Отличаете ли вы хэширование паролей от шифрования и знаете ли, почему для паролей нужны именно медленные функции. Следом спрашивают, чем плох SHA-256 с солью и как перейти на новый алгоритм, не зная паролей пользователей.
Как отвечать
В базе хранят не пароль и не шифротекст, а результат медленной функции с солью:
- Argon2id: первый выбор по рекомендациям OWASP, настраивается по памяти и числу проходов. Минимальная конфигурация там: 19 МиБ памяти, 2 итерации, параллелизм 1;
- bcrypt: проверенный вариант с cost от 10. Учитывайте лимит в 72 байта на входе: после этой границы пароли он не различает;
- scrypt и PBKDF2: тоже допустимы, PBKDF2 берут при требованиях FIPS, для
PBKDF2WithHmacSHA256OWASP называет 600 000 итераций; - соль: уникальная и случайная для каждого пароля, из
SecureRandom. Bcrypt и Argon2 сами кладут её в строку хэша вместе с параметрами, отдельная колонка не нужна.
В Spring Security это PasswordEncoder. PasswordEncoderFactories.createDelegatingPasswordEncoder() хранит в хэше префикс алгоритма ({bcrypt}, {argon2}), поэтому старые и новые хэши живут рядом. Миграция идёт при входе: пароль совпал, хэш устаревший, пересчитываем новым алгоритмом, для этого есть upgradeEncoding().
Про память честный ответ такой: пароль принимают в char[], а не в String, потому что строку нельзя затереть, она живёт до сборки мусора и может попасть в дамп кучи. После использования массив затирают через Arrays.fill(password, '\0'). Но это сужение окна, а не гарантия: фреймворк мог уже создать String при разборе запроса. Поэтому важнее не писать пароль в логи и исключать его из toString() у DTO.
Где спотыкаются
Предлагают SHA-256 с солью или шифрование AES. Быстрый хэш на видеокартах перебирается миллиардами вариантов в секунду, а шифрование обратимо: утечёт ключ, утекут все пароли.
Объясняют char[] тем, что «строка попадёт в пул». Строки, созданные в рантайме, в пул сами не попадают, реальная причина в неизменяемости и времени жизни объекта.
Собираются «перехэшировать всю базу» новым алгоритмом. Без исходных паролей это невозможно, пересчёт делается только при успешном входе пользователя.