Смена пароля: правильный алгоритм действий

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

Библиотека bcrypt.js обеспечивает криптографическое хеширование паролей и их безопасное сравнение, но корректность процесса смены пароля определяется не библиотекой, а архитектурой алгоритма.


Общая логика процесса смены пароля

Типовой процесс обновления пароля включает несколько строго последовательных этапов:

  1. Получение текущего пароля пользователя
  2. Проверка соответствия текущего пароля хешу в базе
  3. Валидация нового пароля
  4. Хеширование нового пароля
  5. Обновление записи в базе данных
  6. Инвалидация активных сессий (при необходимости)

Каждый этап имеет значение для безопасности системы.


Проверка текущего пароля

Первый обязательный шаг — подтверждение личности через текущий пароль.

bcrypt.js предоставляет асинхронную функцию сравнения:

import bcrypt from "bcryptjs";

const isPasswordValid = await bcrypt.compare(plainPassword, user.passwordHash);

Особенности сравнения:

  • сравнение выполняется в константное время
  • исключается утечка информации через timing attacks
  • возвращается только булев результат

При отрицательном результате дальнейшие шаги не выполняются.


Валидация нового пароля

Перед хешированием новый пароль проходит проверку на соответствие требованиям безопасности:

Типовые проверки:

  • минимальная длина (обычно 8–12 символов)
  • наличие цифр и букв
  • запрет простых последовательностей
  • проверка на совпадение с предыдущим паролем

Дополнительно может использоваться проверка истории паролей, чтобы исключить повторное использование последних N значений.


Проверка на повтор старого пароля

Сравнение нового пароля с текущим выполняется через bcrypt.compare:

const isSameAsOld = await bcrypt.compare(newPassword, user.passwordHash);

Если результат истинный, обновление пароля блокируется.

Такой подход предотвращает ситуацию, при которой пользователь “меняет” пароль на тот же самый.


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

После прохождения всех проверок выполняется создание нового хеша.

bcrypt.js использует соль и адаптивную сложность:

const saltRounds = 12;
const newHash = await bcrypt.hash(newPassword, saltRounds);

Важные параметры:

  • saltRounds определяет вычислительную сложность
  • чем выше значение, тем выше безопасность и нагрузка
  • типичные значения: 10–14

Соль генерируется автоматически и включается в итоговый хеш.


Обновление записи в базе данных

После получения нового хеша выполняется атомарное обновление записи пользователя:

await User.updateOne(
  { _id: user.id },
  { passwordHash: newHash }
);

Критические требования:

  • операция должна быть атомарной
  • исключается частичное обновление данных
  • важно предотвращать race conditions при параллельных запросах

В системах с высокой нагрузкой часто используется версия записи (optimistic locking).


Инвалидация активных сессий

После смены пароля необходимо учитывать уже существующие сессии пользователя.

Типовые стратегии:

  • удаление всех refresh-токенов
  • обновление версии сессии
  • принудительный logout всех устройств
  • привязка сессии к hash пароля

Пример логики через версию пароля:

await User.updateOne(
  { _id: user.id },
  {
    passwordHash: newHash,
    passwordVersion: user.passwordVersion + 1
  }
);

При проверке токена сравнивается сохранённая версия.


Полный алгоритм смены пароля

Обобщённая последовательность:

  1. Получение пользователя по идентификатору
  2. Проверка текущего пароля через bcrypt.compare
  3. Проверка нового пароля на правила безопасности
  4. Проверка совпадения нового и старого пароля
  5. Хеширование нового пароля через bcrypt.hash
  6. Обновление passwordHash в базе данных
  7. Обновление версии сессии или инвалидирование токенов

Обработка ошибок и защита от атак

Корректная реализация должна учитывать несколько классов атак:

Подбор текущего пароля

bcrypt защищает от быстрого перебора, но система должна:

  • ограничивать количество попыток
  • использовать rate limiting
  • вводить задержки при ошибках

Replay-атаки

Повторный запрос смены пароля должен быть защищён:

  • проверкой CSRF-токена
  • проверкой актуальности сессии
  • одноразовыми операциями (transaction-like flow)

Гонки запросов

При параллельной смене пароля возможны состояния race condition:

Решение:

  • optimistic locking
  • проверка версии пароля перед обновлением

Производительность bcrypt при смене пароля

bcrypt является вычислительно затратным алгоритмом.

Факторы влияния:

  • saltRounds
  • нагрузка на CPU
  • количество одновременных операций

Рекомендации:

  • использовать асинхронные методы bcrypt.js
  • не блокировать event loop
  • ограничивать параллельные hash-операции на сервере

Типичная ошибка реализации

Наиболее распространённая ошибка — пропуск проверки текущего пароля при смене.

Это приводит к ситуации, когда любой, получивший доступ к сессии, может сменить пароль без подтверждения.

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


Хранение и политика паролей

При смене пароля часто внедряется политика хранения истории:

  • хранение последних N хешей
  • запрет повторного использования
  • периодическая ротация

Проверка выполняется через bcrypt.compare для каждого сохранённого хеша.


Особенности использования bcrypt.js

bcrypt.js работает в JavaScript без нативных зависимостей, что делает его удобным для:

  • Node.js серверов
  • серверлесс функций
  • браузерных окружений (ограниченно)

Однако производительность ниже, чем у нативного bcrypt, что важно учитывать при высокой нагрузке.


Итоговая архитектурная модель

Корректная реализация смены пароля строится вокруг трёх принципов:

  • обязательная проверка текущего пароля через bcrypt.compare
  • генерация нового хеша через bcrypt.hash с контролируемой сложностью
  • обязательная синхронизация состояния сессий после обновления

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