# Как хранить пароли?

Это один из классических вопросов на собеседовании AppSec специалисту.&#x20;

## Не хранить пароли

Лучший ответ: "пароли хранить не надо, потому что в 202X мы не должны запускать сервисы с паролями!". За это можно получить хороший плюс.&#x20;

### 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/>
