Математическая основа хэширования

Криптографическая хэш-функция принимает на вход массив данных произвольного размера и преобразует его в битовую строку строго определённой длины. Алгоритм спроектирован так, чтобы работать строго в одну сторону. Зная результат, вычислить исходное сообщение невозможно за разумное время на существующих вычислительных мощностях.

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

Эпоха быстрых хэшей и почему они провалились

В девяностые годы разработчики массово сохраняли пароли с помощью алгоритмов MD5 и SHA-1. Рональд Ривест разработал MD5 в 1991 году, а Национальный институт стандартов и технологий США представил SHA-1 в 1995 году. Эти функции создавались для проверки целостности файлов и вычисления цифровых подписей, поэтому инженеры стремились сделать их максимально быстрыми.

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

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

Борьба со словарями с помощью соли и перца

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

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

Дополнительным барьером служит перец. Это секретная константа, которая также конкатенируется с паролем, но хранится отдельно от базы данных, например в защищённом модуле HSM или защищённых переменных окружения сервера приложений. Если злоумышленник скачивает базу данных через SQL-инъекцию, он не видит перец и не может начать перебор паролей на своём оборудовании.

Замедление вычислений в PBKDF2 и Bcrypt

Добавление соли сделало невозможными атаки по готовым таблицам, однако перебор грубой силой на мощных чипах оставался угрозой. Возникла необходимость намеренно замедлить процесс вычисления хэша на стороне сервера.

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

В 1999 году Нильс Провос и Дэвид Мазирес представили алгоритм Bcrypt, основанный на симметричном шифре Blowfish. Алгоритм содержит настраиваемый параметр трудоёмкости, увеличивающий количество внутренних раундов экспоненциально. Главной особенностью Bcrypt стала требовательность к архитектуре процессора за счёт частого обновления состояния подстановок в памяти.

Аппаратные атаки и появление функции Scrypt

С развитием интегральных схем специального назначения ASIC и программируемых логических матриц FPGA алгоритмы вроде PBKDF2 перестали гарантировать абсолютную защиту. Специализированные микросхемы выполняли миллионы итераций хэширования параллельно на крошечных кристаллах кремния, поскольку PBKDF2 практически не расходует оперативную память.

Для устранения этого пробела Колин Персиваль в 2009 году разработал алгоритм Scrypt. Его ключевая идея заключается в концепции функций, требовательных к оперативной памяти. Scrypt сначала заполняет большой массив в памяти псевдослучайными битами, а затем читает их в непредсказуемом порядке.

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

Современный золотой стандарт Argon2

В 2015 году завершился открытый международный конкурс Password Hashing Competition. Победителем признали алгоритм Argon2, созданный группой исследователей Люксембургского университета под руководством Алекса Бирюкова. Алгоритм обеспечивает максимальную гибкость настройки безопасности под конкретное оборудование.

Argon2 оперирует тремя независимыми параметрами конфигурации. Администратор задаёт процессорное время через число проходов, количество выделяемой оперативной памяти и степень параллелизма потоков выполнения. Подобное разделение позволяет эффективно нагружать все ядра сервера и противостоять параллельным вычислениям на GPU.

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

  • Версия Argon2d обращается к массивам памяти на основе значений предыдущих вычислений, что максимизирует сопротивление графическим процессорам, но создаёт теоретическую уязвимость к атакам по сторонним каналам тайминга кэш-памяти.
  • Версия Argon2i использует независимый от данных порядок адресации памяти, полностью исключая утечки информации о времени ответа системы при анализе процессорного кэша.
  • Гибридная версия Argon2id комбинирует оба подхода, проходя первую половину первой итерации в режиме защиты от тайминговых атак, а оставшиеся раунды в режиме максимальной защиты от специализированных микросхем.

Частые вопросы

Можно ли восстановить исходный пароль из хэша, если пользователь его забыл?

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

Помогает ли повторное хэширование MD5 от MD5?

Многократное применение быстрого алгоритма не спасает от взлома. Даже миллион итераций MD5 вычисляются на современных GPU быстрее, чем один раунд специализированного защищённого алгоритма Argon2.

Зачем хэшировать пароль на стороне браузера перед отправкой на сервер?

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

Почему алгоритмы семейства SHA-256 и SHA-512 не рекомендуют для паролей?

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