SHA-256 и MD5 для паролей: почему это ошибка

Использование MD5 и SHA-256 для хранения паролей встречается в старых системах и до сих пор повторяется в учебных примерах, что создаёт опасную иллюзию допустимости такого подхода. Оба алгоритма относятся к криптографическим хэш-функциям общего назначения, но не предназначены для защиты паролей.

Ключевая проблема заключается не в математической слабости самих алгоритмов в классическом смысле, а в их скорости. Они вычисляются слишком быстро, что делает их идеальной целью для перебора.


Разница между криптографическим хэшем и хэшированием паролей

Криптографические хэш-функции (MD5, SHA-1, SHA-256) создавались для:

  • проверки целостности данных
  • цифровых подписей
  • контроля неизменности файлов

Требования к ним:

  • детерминированность
  • высокая скорость вычисления
  • устойчивость к коллизиям (в идеале)

Но для паролей требуется противоположное:

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

Хэширование паролей — это не просто “перевод строки в фиксированный набор байт”, а специально усложнённый процесс защиты от атак перебором.


MD5: устаревший и небезопасный инструмент

MD5 сегодня не рекомендуется использовать ни в каком криптографическом контексте, связанном с безопасностью.

Основные проблемы:

  • высокая скорость вычисления (миллионы операций в секунду на GPU)
  • наличие коллизий
  • предсказуемость атак через радужные таблицы
  • мгновенный перебор коротких паролей

Любой пароль, обработанный MD5, может быть восстановлен за короткое время с использованием готовых баз.


SHA-256: ложное ощущение безопасности

SHA-256 считается криптографически стойким алгоритмом, но это не делает его подходящим для паролей.

Основная ошибка использования SHA-256 для паролей:

  • отсутствие встроенного замедления
  • отсутствие механизма адаптивной сложности
  • возможность параллельного перебора на GPU и ASIC

Пример уязвимости:

  • короткий пароль → перебор за секунды или минуты
  • средний пароль → перебор в разумные сроки при использовании GPU-кластеров
  • повторное использование паролей → мгновенное раскрытие множества аккаунтов

SHA-256 защищает данные от изменения, но не защищает от перебора.


Радужные таблицы и предвычисленные атаки

Для MD5 и SHA-256 существуют огромные базы предвычисленных хэшей.

Суть атаки:

  • берётся словарь паролей
  • вычисляются их хэши заранее
  • создаётся таблица соответствий “хэш → пароль”
  • при утечке базы выполняется мгновенный поиск совпадения

Без соли такие атаки работают практически мгновенно.


GPU и массовый перебор

Современные видеокарты и специализированные устройства позволяют:

  • выполнять миллиарды хэш-операций в секунду
  • распараллеливать перебор
  • атаковать базы данных в реальном времени

Алгоритмы вроде SHA-256 не ограничивают скорость атакующего, что делает их непригодными для защиты паролей.


Соль как базовый элемент защиты

Соль — это случайная строка, добавляемая к паролю перед хэшированием.

Пример:

hash = SHA256(password + salt)

Назначение соли:

  • делает каждый хэш уникальным
  • ломает радужные таблицы
  • усложняет массовые атаки

Но даже с солью SHA-256 остаётся слишком быстрым, что не решает проблему полностью.


bcrypt.js как специализированное решение

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

Ключевые свойства bcrypt:

  • встроенная соль (генерируется автоматически)
  • адаптивная сложность (cost factor)
  • намеренно медленное вычисление
  • устойчивость к GPU-ускоренным атакам

bcrypt основан на алгоритме Blowfish и использует многократное “растягивание” ключа.


Принцип работы bcrypt

bcrypt выполняет следующие шаги:

  • генерация соли
  • многократное преобразование ключа
  • применение адаптивного количества раундов
  • формирование итогового хэша

Важный параметр — cost factor:

2^cost = количество раундов

Чем выше cost, тем медленнее вычисление и тем выше безопасность.


Установка bcrypt.js

npm install bcryptjs

Хэширование пароля с bcrypt.js

const bcrypt = require('bcryptjs');

const password = 'user_password';

const salt = bcrypt.genSaltSync(10);
const hash = bcrypt.hashSync(password, salt);

console.log(hash);

Автоматическая генерация соли:

const hash = bcrypt.hashSync(password, 10);

Проверка пароля

const isValid = bcrypt.compareSync('user_password', hash);

console.log(isValid); // true или false

bcrypt автоматически извлекает соль из хэша и использует её при проверке.


Асинхронное использование

Синхронные методы блокируют поток выполнения, что критично в серверных приложениях.

Асинхронный вариант:

const bcrypt = require('bcryptjs');

bcrypt.hash('password', 10, (err, hash) => {
  if (err) throw err;

  bcrypt.compare('password', hash, (err, result) => {
    console.log(result);
  });
});

Выбор cost factor

Типичные значения:

  • 8 — тестовые окружения
  • 10 — баланс производительность/безопасность
  • 12–14 — повышенная безопасность
  • 16+ — высокая нагрузка на сервер

Рост cost увеличивает время вычисления экспоненциально.


Ошибки при использовании bcrypt.js

Использование слишком низкого cost

Снижение cost ради производительности приводит к ослаблению защиты.


Хранение пароля без хэширования

Любые “ускорения” через хранение plaintext-паролей полностью уничтожают безопасность системы.


Повторное использование одной соли вручную

bcrypt уже включает соль. Внешнее управление солью нарушает модель безопасности.


Сравнение хэшей через обычное равенство

if (hash === userInputHash)

Такой подход не работает, так как bcrypt включает случайные элементы.


Почему bcrypt.js лучше SHA-256 и MD5 для паролей

Сравнение характеристик:

  • MD5: быстрый, взламывается мгновенно
  • SHA-256: быстрый, уязвим к GPU-перебору
  • bcrypt: медленный, адаптивный, устойчивый к массовым атакам

Главное отличие bcrypt — контроль времени вычисления, а не только криптографическая стойкость.


Когда SHA-256 всё же уместен

SHA-256 остаётся полезным для:

  • проверки целостности файлов
  • цифровых подписей
  • хэширования данных, не связанных с секретами пользователей

Но не для хранения паролей.


Архитектурная модель безопасного хранения паролей

Правильный подход:

  • bcrypt или аналог (argon2, scrypt)
  • уникальная соль на каждый пароль
  • адаптивная сложность
  • ограничение попыток входа
  • защита от брутфорса на уровне сервера

Использование MD5 или SHA-256 в этой модели является архитектурной ошибкой, приводящей к компрометации системы при утечке базы данных.