Как безопасно хранить пароли в базе данных: сравнение bcrypt, scrypt, Argon2id и PBKDF2 в 2026 году | AdminWiki

Как безопасно хранить пароли в базе данных: сравнение bcrypt, scrypt, Argon2id и PBKDF2 в 2026 году

15 сентября 2026 12 мин. чтения

Прямой ответ на главный вопрос: в 2026 году для хранения паролей в базе данных берите Argon2id с memory cost 64 MiB, iterations 3 и parallelism 2. Если Argon2id недоступен, используйте scrypt с N=2^17, r=8, p=1. bcrypt с cost factor от 12 остается рабочим вариантом для legacy-систем, PBKDF2 с 600 000 итераций HMAC-SHA256 уместен только там, где требуется соответствие FIPS. MD5, SHA-1 и одиночный SHA-256 для паролей не подходят.

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

Ниже разобрано, чем опасны быстрые хеши, как сравнивать bcrypt, scrypt, Argon2id и PBKDF2, как подобрать параметры под реальную нагрузку и что проверить в текущей схеме хранения паролей.

Почему открытое хранение паролей и быстрые хеши - это риск

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

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

Чем опасны MD5 и SHA-1 для паролей

Скорость. Одна видеокарта класса NVIDIA RTX 4090 считает десятки миллиардов MD5-хешей в секунду, для SHA-1 порядок тот же, но в несколько раз ниже. Пространство перебора для 8-символьного пароля из строчных латинских букв и цифр содержит 36^8, около 2,8 трлн комбинаций. При скорости 10^10-10^11 хешей в секунду полный перебор укладывается в минуты, а среднее время поиска вдвое меньше.

Соль эту проблему не закрывает. Она защищает от заранее просчитанных rainbow tables и мешает вскрыть одинаковые пароли одним вычислением, но перебор конкретного аккаунта идет с той же скоростью.

У MD5, SHA-1 и одиночного SHA-256 нет параметра стоимости. Замедлить их, не меняя алгоритм, невозможно, поэтому с ростом вычислительной мощности стойкость хранилища падает каждый год.

Практический пример: утечка LinkedIn в 2012 году. Пароли хранили как SHA-1 без соли, миллионы учетных записей вскрыли перебором по словарю на обычных GPU. Та же история повторяется с любой базой, где в поле пароля лежит MD5 или SHA-1.

Отдельный случай - встроенные функции СУБД. В MySQL и MariaDB функции PASSWORD() и SHA2() не годятся для паролей пользователей: они либо устарели, либо считают быстрый хеш без растяжения. Разбор функций, прав доступа и переноса хеширования в приложение есть в статье про хранение паролей в MySQL и MariaDB.

Руководство OWASP по хранению паролей ставит Argon2id первым выбором, а NIST SP 800-63B требует соль и растяжение ключа для всех паролей. На эти документы удобно ссылаться, когда выбор алгоритма проходит внутренний аудит или проверку на соответствие PCI DSS.

Что такое соль и почему она обязательна

Соль - случайная строка, уникальная для каждого пароля, длиной от 16 байт, сгенерированная криптографически стойким генератором (CSPRNG). Хранится она рядом с хешем в открытом виде, и это нормально: секретность соли не нужна, важна уникальность.

Два пользователя с одинаковым паролем получат разные хеши, поэтому одна найденная строка словаря не открывает однотипные аккаунты. bcrypt и Argon2 включают соль в выходную строку автоматически, у PBKDF2 соль передается отдельным параметром, и хранить ее приходится самому.

Генерация соли, хранение перца в KMS и Vault, типичные ошибки при работе с ними разобраны в отдельном материале про соль и перец в хешировании паролей.

Обзор алгоритмов хеширования паролей: bcrypt, scrypt, Argon2id, PBKDF2

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

Argon2id: рекомендации OWASP и параметры по умолчанию

Argon2id победил в Password Hashing Competition в 2015 году и описан в RFC 9106. Гибридный режим соединяет устойчивость Argon2i к атакам по сторонним каналам и сопротивление Argon2d к перебору на GPU. Настройка идет тремя параметрами: memory cost (в MiB), iterations (time cost) и parallelism (число потоков).

OWASP приводит минимальный порог: memory cost 19 MiB (19456 KiB), iterations 2, parallelism 1. Для чувствительных сервисов рекомендуются memory cost 64 MiB, iterations 3, parallelism 4. Значения не догма: подбирайте их так, чтобы одно хеширование занимало 250-500 мс на продакшн-сервере.

Библиотеки: libsodium (crypto_pwhash), argon2-cffi и passlib для Python, PASSWORD_ARGON2ID в PHP, пакет argon2 в Node.js, x/crypto/argon2 в Go. Строки хешей переносимы между языками, поэтому миграция между стеками не требует пересчета паролей.

bcrypt: ограничения и актуальность в 2026 году

bcrypt появился в 1999 году на основе шифра Blowfish и остается самым распространенным вариантом. Параметр cost factor (work factor) задает число раундов как 2^cost, время вычисления растет экспоненциально. Для 2026 года рабочий диапазон 12-14: cost 12 дает около 250 мс на современном серверном CPU, cost 14 примерно в четыре раза дольше.

Ограничение известно: bcrypt обрезает пароль до 72 байт. Для длинных passphrase хвост строки не влияет на хеш. Обходной путь - сначала посчитать HMAC-SHA256 от пароля и закодировать результат в base64, затем подать его на вход bcrypt.

Второе ограничение: bcrypt не memory-hard. Основная стоимость - время CPU, память почти не расходуется, поэтому FPGA и ASIC получают преимущество перед обычными GPU. Для legacy-систем это приемлемо, для новых проектов Argon2id предпочтительнее. Поддержка: bcrypt в Node.js, passlib в Python, password_hash с PASSWORD_BCRYPT в PHP.

scrypt: memory-hard подход и его особенности

scrypt предложил Колин Персивал в 2009 году, спецификация закреплена в RFC 7914. Алгоритм memory-hard: параметр N задает число блоков, r - размер блока, p - степень параллелизма. Расход памяти считается как 128 × N × r байт, поэтому подъем N прямо увеличивает требования к RAM и делает перебор на GPU и ASIC невыгодным.

Практичные значения: N=2^17 (131072), r=8, p=1. Это около 128 MiB памяти на одно вычисление и задержка в сотни миллисекунд.

scrypt есть в OpenSSL (EVP_PBE_scrypt) и libsodium (crypto_pwhash_scryptsalsa208sha256), что позволяет обойтись без сторонних библиотек. Осторожность нужна при выборе N: слишком большое значение исчерпает память сервера на пиковой нагрузке. ASIC для майнинга Litecoin рассчитаны на низкомные параметры (N=1024, r=1, p=1) и к рекомендованным настройкам неприменимы.

PBKDF2: когда он всё ещё уместен

PBKDF2 стандартизирован в RFC 8018 и входит в требования FIPS 140-2/140-3, из-за чего часто остается единственным допустимым вариантом в регулируемых средах. Работает он на HMAC (чаще HMAC-SHA256), главный параметр - число итераций.

OWASP для 2026 года называет минимум 600 000 итераций для PBKDF2-HMAC-SHA256. На современном серверном CPU это 0,3-0,5 с, что попадает в целевой диапазон задержки. Слабое место - низкое потребление памяти: GPU-ферма считает итерации параллельно, и разрыв между CPU и GPU здесь выше, чем у memory-hard алгоритмов.

Вывод по применимости: PBKDF2 держите там, где есть требование FIPS или где другие реализации недоступны. Для новых проектов без таких ограничений выбор сводится к Argon2id, scrypt и bcrypt.

Сравнение по ключевым критериям:

АлгоритмГодПараметры стоимостиПамять на одно хешированиеСтойкость к GPU/ASICСтандарт и статус
Argon2id2015memory cost, iterations, parallelism19-64 MiB и выше, задается вручнуюВысокаяRFC 9106, первый выбор OWASP
scrypt2009N, r, pоколо 128 MiB при N=2^17, r=8ВысокаяRFC 7914
bcrypt1999cost factor 4-31менее 1 MiBСредняяде-факто стандарт, RFC нет
PBKDF22000iterations, saltменее 1 MiBНизкаяRFC 8018, требования FIPS
MD5, SHA-11992, 1995отсутствуютменее 1 MiBНе защищаетНе применять для паролей

Как подобрать параметры хеширования под реальную нагрузку

Ориентир один: одно хеширование на продакшн-железе должно занимать 250-500 мс. Меньше - атакующему дешевле перебирать, больше - растет риск, что пик входов положит аутентификацию.

Методика бенчмарка: измеряем время хеширования

Порядок действий:

  1. Возьмите инстанс того же класса, что и продакшн: те же CPU, объем RAM, тип диска и та же виртуализация.
  2. Запустите 100 хеширований с целевыми параметрами и запишите время каждого прогона.
  3. Смотрите на среднее и на 95-й перцентиль: первое показывает типичную задержку, второй - поведение под нагрузкой.
  4. Повторите замер при конкурентной нагрузке, например при работающих соседних сервисах.

Пример замера для Argon2id на Python:

import time
import argon2

ph = argon2.PasswordHasher(time_cost=3, memory_cost=65536, parallelism=2)
samples = []
for _ in range(100):
    start = time.perf_counter()
    ph.hash("benchmark-password")
    samples.append(time.perf_counter() - start)

samples.sort()
avg = sum(samples) / len(samples) * 1000
p95 = samples[94] * 1000
print("avg: %.0f ms, p95: %.0f ms" % (avg, p95))

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

Влияние параметров на память и CPU

Argon2id: memory cost задает расход RAM на одно хеширование напрямую, iterations добавляют время CPU, parallelism определяет число потоков и одновременно умножает потребление памяти. При m=64 MiB и p=2 одно вычисление забирает около 128 MiB.

scrypt: N и r влияют и на память, и на CPU, p добавляет параллелизм. bcrypt расходует только CPU, память почти не задействована. У PBKDF2 стоимость тоже целиком в CPU.

Считайте пиковую нагрузку. Сто одновременных входов с Argon2id при m=64 MiB дают 6,4 ГиБ памяти в моменте, у bcrypt cost 12 та же сотня входов укладывается в десятки мегабайт, но загружает все ядра. Отсюда два вывода: для memory-hard алгоритмов нужен запас RAM и очередь на аутентификацию, для CPU-зависимых - лимит на число одновременных проверок.

Rate limiting, блокировка аккаунтов после серии неудач и защита от timing-атак снижают нагрузку на хеширование и закрывают онлайн-перебор. Готовые конфигурации для Nginx и fail2ban, а также значения cost factor для bcrypt и Argon2id собраны в руководстве по защите хранилища паролей от брутфорса.

В Kubernetes ограничивайте ресурсы pod-ов, которые занимаются аутентификацией. CPU-лимит ниже реальной потребности приводит к троттлингу и скачкам задержки, а memory-лимит ниже memory cost Argon2id заканчивается OOMKilled на первом же пике. Параметры пересматривайте раз в 1-2 года и после каждой смены железа.

Практическая реализация: хранение, миграция и обновление хешей

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

Формат хранения хеша и соли

Argon2id с memory cost 64 MiB, iterations 3, parallelism 2:

$argon2id$v=19$m=65536,t=3,p=2$c29tZXNhbHR2YWx1ZQ$aGFzaGVkLXBhc3N3b3JkLXZhbHVl

bcrypt с cost 12:

$2b$12$LQv3c1yqBWVHxkd0LHAkCOYz6TtxMQJqhN8/LewdBPj4J/HS.iK2a

Поле в базе: VARCHAR(255) хватает и для Argon2id с крупными параметрами, и для bcrypt. Если схема использует BYTEA, длина та же, отличается только кодировка. Типы полей, индексы и защита от утечек через логи и бэкапы описаны в материале про схему таблицы пользователей для безопасного хранения паролей.

Pepper хранится отдельно от базы: в KMS, Vault или переменных окружения приложения. Перед хешированием к паролю применяют HMAC-SHA256 с ключом-перцем. Это дает выигрыш при утечке только дампа: без ключа перебор невозможен, а при использовании bcrypt попутно решается проблема 72 байт, потому что на вход подается base64-строка фиксированной длины.

Миграция на новый алгоритм без сброса паролей

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

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

Пример на Python с passlib:

from passlib.context import CryptContext

ctx = CryptContext(
    schemes=["argon2", "bcrypt"],
    deprecated=["bcrypt"],
    argon2__time_cost=3,
    argon2__memory_cost=65536,
    argon2__parallelism=2,
)

ok, new_hash = ctx.verify_and_update(plain_password, stored_hash)
if ok and new_hash:
    update_password_hash(user_id, new_hash)

Комментарий к коду: verify_and_update возвращает флаг успешной проверки и новую строку хеша, если текущая схема устарела. Отдельное поле hash_version или password_algo нужно тогда, когда префикса недостаточно, например при хранении SHA-1 без соли. Порядок шагов, метрики и план отката разобраны в статье про миграцию паролей на Argon2id и bcrypt без сброса.

Контрольные точки при миграции: доля аккаунтов на новом алгоритме растет по метрике в дашборде, ошибки проверки не превышают базовый уровень, откат не требует обратного перехеширования и сводится к возврату старых параметров проверки. Для пользователей, которые не входили больше 6-12 месяцев, разумно инициировать сброс пароля при следующей попытке входа. Черновые скрипты миграции и разбор ошибок удобно прогонять через LLM: агрегатор AiTunnel дает единый доступ к 200+ моделям GPT, Gemini и Claude с оплатой в рублях и без VPN, что экономит время на ревью кода аутентификации.

Чек-лист для аудита текущей схемы хранения паролей

Двенадцать проверок, которые проходятся за один вечер по текущей базе и коду аутентификации.

  1. Пароли не хранятся в открытом виде и не шифруются обратимым ключом. Проверяется по коду записи и чтения поля.
  2. Используется медленный алгоритм: Argon2id, scrypt, bcrypt или PBKDF2.
  3. MD5, SHA-1 и одиночный SHA-256 без растяжения не применяются ни в одном месте схемы.
  4. Соль уникальна для каждого пароля, длина от 16 байт, генерация через CSPRNG.
  5. Параметры соответствуют рекомендациям OWASP на 2026 год: Argon2id m не ниже 19 MiB, t не ниже 2, p не ниже 1; bcrypt cost от 12; PBKDF2-HMAC-SHA256 от 600 000 итераций; scrypt N=2^17, r=8.
  6. Время одного хеширования на продакшн-железе укладывается в 250-500 мс.
  7. Параметры можно поднять без сброса паролей: есть поле версии хеша или определение алгоритма по префиксу.
  8. Pepper, если используется, хранится вне базы: KMS, Vault, переменные окружения, и не попадает в резервные копии.
  9. Доступ к таблице пользователей ограничен отдельной ролью, у приложения нет лишних прав на чтение.
  10. Пароли и хеши не попадают в логи, трассировки, дампы и сообщения об ошибках.
  11. Бенчмарк проведен на целевом инстансе с учетом пиковой нагрузки и числа одновременных входов.
  12. Аудит повторяется раз в 6-12 месяцев, после смены железа и после каждого инцидента.

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

Итог: что выбрать в 2026 году

Для нового проекта: Argon2id с memory cost 64 MiB, iterations 3, parallelism 2 и выше, если позволяет железо. Минимальный порог OWASP (m=19 MiB, t=2, p=1) подходит для сервисов с жесткими ограничениями по RAM, но запас лучше держать выше минимума.

Если Argon2id недоступен: scrypt с N=2^17, r=8, p=1. bcrypt с cost factor от 12 остается допустимым для legacy и для сред, где менять библиотеки дорого. PBKDF2 с 600 000 итераций HMAC-SHA256 применяйте только под требование FIPS. MD5 и SHA-1 не используйте ни в каком виде, включая встроенные функции СУБД.

Дальше три действия: замерить хеширование на продакшн-железе и выставить параметры под 250-500 мс, настроить перехеширование при входе, пройти по чек-листу из 12 пунктов. Параметры стоит пересматривать раз в 1-2 года: железо дешевеет, стоимость перебора падает, а хеши в базе остаются теми же.

Поделиться:
Сохранить гайд? В закладки браузера