Как хранить пароли?
Это один из классических вопросов на собеседовании AppSec специалисту.
Не хранить пароли
Лучший ответ: "пароли хранить не надо, потому что в 202X мы не должны запускать сервисы с паролями!". За это можно получить хороший плюс.
WebAuthn
Два стандарта для беспарольной аутентификации на сайтах, в мобильных и веб-приложениях: WebAuthn API и CTAP. Оба были одобрены Microsoft, Mozilla и Google. Как они работают: https://habrahabr.ru/post/353966/?utm_campaign=353966
В браузере создаются пара ключей, на сервере храним только публичный ключ.
Одна из проблем, почему это не получило должного распространения: невозможность шарить сессию между устройствами. Недавно появилась технология passkeys от Apple, возможно это решит проблему.
Вход по СМС или TOTP
Если все же надо
Но если все-таки вернуться к вопросу, то ожидается ответ в духе:
Хранить хеши-пароли с солью
В качестве хеш-функции использовать Argon2, PBKDF2, Bcrypt, Scrypt с оптимальным количеством раундов
Соль отдельная для каждого пользователя
Харденинг
например, использовать HSM (тут долгие холивары в каком режиме)
"pepper" — то, что добавляют к паролю в начало (соль в конце, грубо говоря)
Пароли-канарейки: простые пароли в базе, которые добавляются периодически для пользователей, которые в нормальной жизни никогда не будут авторизоваться под ними, если кто-то авторизуется с этим паролем — значит у вас унесли базу и можно будет предположить примерное время
Ответы — хранить соленный md5/sha1/sha256/sha512 — автоматически ставят жирный минус.
Защита от брутфорса
Защита от брутфорса (все пароли для одного пользователя, рейтлимиты и капчи) и обратного брутфорса (слабые пароли на всех пользователях)
Как защитить от RCE на бэкенде
Но также есть еще один вопрос, сложный, и ответ на него мало кто знает — ”А как нам хранить пароли так, что если атакующий получит RCE на бэкендах, в т.ч. root привилегии, то не сможет дампнуть табличку с хэшами?”
Есть очень легкий, понятный и технически верный путь, как решить поставленную задачу. Нам нужно вынести менеджмент паролей в отдельный сервис (или база с возможностью запуска только хранимых процедур), тем самым отобрать у бэкенд права на получение всех хешей пользователей юзера и дать две функции ( или хранимые SQL процедуры):
getSalt(user_id)
— Вернет «соль» пароля на бэкенд по userId, который пытается войти. Бэкенд возьмет введенный юзером пароль и получит хэш с солью из базыcheckPassword(user_id, hashed_password)
— Возвращает true/false на проверке хэша пароля на предыдущем шаге у user_id
Таким образом, забрать хеши не получится для локального перебора.
Еще нужна процедура на создание пользователя и смену пароля, то они также не позволяют select'нуть хэши паролей пользователей. Хорошая статья по теме (но там не всё): https://www.secjuice.com/secure-password-handling/
Last updated