Руководства по безопасности

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

Как безопасно хранить пароли: почему открытый текст, шифрование и простые хеши не подходят, зачем нужна соль и как выбрать bcrypt, Argon2 или scrypt.

Опубликовано 2026-06-27 Обновлено 2026-10-06 8 мин чтения
На этой странице

«Как именно хранить пароли пользователей в базе данных?» — каждый создатель однажды натыкается на этот вопрос. Ответ ясен. Вот безопасный способ их хранить, по порядку, без шагов атаки.

Почему открытый текст, шифрование и сырые хеши все проваливаются

Предположим, что база данных однажды утечёт. Когда это случится, ущерб сильно различается по методу хранения.

  • Открытый текст: каждый пароль раскрыт в тот же миг, как он утёк. Хуже того, переиспользование паролей сцепляет утечку с другими сервисами пользователя. Худший случай.
  • Шифрование (обратимое): ключ возвращает всё назад — поэтому если ключ утечёт вместе с ним, вы снова в открытом тексте. Пароль никогда не нужно читать обратно, так что обратимость ничего вам не даёт.
  • Сырой хеш (MD5/SHA-256): быстрый хеш позволяет атакующему быстро проверять догадки. Распространённые пароли падают перед радужными таблицами и перебором.

Четыре стадии к безопасному хранению

Быстрее всего понять это как добавление по одному исправлению к слабому методу.

1. открытый текст: при утечке читаются все пароли
↓ сделать односторонним
2. сырой хеш: слаб к радужным таблицам
↓ добавить соль для каждого пользователя
3. хеш с солью: таблицы побеждены, но перебор всё ещё быстр
↓ сделать намеренно медленным
4. хеш с солью + медленный (Argon2id / bcrypt): это и есть ответ
открытый текст → сырой хеш → с солью → медленный хеш. Только в конце это действительно «безопасное хранение».

Ключевое понимание: соль и медленный хеш делают разную работу. Соль побеждает предвычисление (радужные таблицы) и массовый взлом за счёт переиспользования. Медленный хеш роняет перебор до непрактичной скорости. Вам нужны оба, прежде чем это станет безопасным (→ что такое хеширование).

На практике: что делать сейчас

1

Используйте Argon2id (или bcrypt)

Для новых систем сделайте Argon2id первым выбором — он может требовать ещё и память, что сопротивляется перебору на GPU. Если вы предпочитаете хорошо проверенную реализацию, bcrypt на практике более чем достаточно. Используйте реализацию из стандартной библиотеки как есть для любого из них.

2

Пусть соль генерирует библиотека

bcrypt/Argon2 генерируют соль для каждого пользователя и встраивают её внутрь строки хеша. Вы не держите отдельный столбец для соли. Самодельный MD5(salt + password) — классика того, чего делать не надо.

3

Настройте стоимость под вашу среду

Задайте фактор стоимости bcrypt или память/итерации/параллелизм Argon2 настолько высоко, насколько можно, пока легитимный вход всё ещё ощущается мгновенным. Серверы становятся быстрее — но и атакующие тоже; пересматривайте и повышайте это ежегодно.

4

Проверяйте стандартной функцией сравнения

При входе сопоставляйте ввод с сохранённым хешем функцией проверки из библиотеки (большинство используют безопасное по времени сравнение с постоянным временем). Не пишите своё сравнение строк.

5

Переносите слабые хеши перехешированием при входе

Если вы уже храните MD5/SHA-256, в тот миг, когда пользователь успешно входит, перехешируйте новой схемой и сохраните. Тех, кто не входил, можно пока усилить, повторно захешировав существующий хеш с помощью bcrypt (слоёный хеш).

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

Типичная ошибка

  • Шифровать пароли для хранения (ключ утёк → конец)
  • Хранить MD5(password) или SHA-256(password) напрямую
  • Использовать одну общую соль для всех пользователей
  • Самодельный hash(salt + password)

Правильная реализация

  • Односторонний хеш с Argon2id / bcrypt
  • Соль для каждого пользователя (библиотека добавляет её автоматически)
  • Стоимость настолько высока, насколько комфортно, повышается периодически
  • Использовать стандартные функции хеширования/проверки как есть

Хеширование, шифрование и кодирование

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

ХешированиеШифрованиеКодирование (Base64 и т. п.)
Обратимо?Нет (одностороннее)Да, с ключомДа, кем угодно
КлючНетНуженНет
Типичное применениеХранение паролей, обнаружение подменыКонфиденциальность данных или трафикаИзменение формата данных
Для хранения паролейИспользовать (соль + медленный хеш)Не использоватьНе использовать (то же, что открытый текст)

Рекомендуемые настройки (OWASP)

Минимальные настройки из OWASP Password Storage Cheat Sheet. Считайте их нижней границей и повышайте под свой сервер.

АлгоритмМинимальная настройкаКогда использовать
Argon2id19 МиБ памяти, 2 итерации, параллелизм 1Первый выбор для новых систем
scryptN=2^17 (128 МиБ), r=8, p=1Когда Argon2id недоступен
bcryptСтоимость 10 или выше. Ввод ограничен 72 байтамиСуществующие системы, проверенная репутация
PBKDF2-HMAC-SHA256600 000 итераций или большеКогда требуется соответствие FIPS 140

Проверьте также встроенные средства вашей платформы. password_hash() в PHP по умолчанию использует bcrypt (PASSWORD_DEFAULT), а в PHP 8.4 стоимость по умолчанию поднята с 10 до 12. Django по умолчанию использует PBKDF2; чтобы перейти на Argon2, установите django[argon2] и поставьте Argon2 первым в PASSWORD_HASHERS. Django автоматически перехеширует пароли, сохранённые старым алгоритмом, когда пользователь входит.

Как проверить свой сервис

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

Как выглядит хешСхемаЧто делать
$argon2id$v=19$m=…,t=…,p=…$Argon2idПроверьте, что m, t и p не ниже минимума OWASP
$2y$12$… / $2b$10$…bcrypt (число — стоимость)Повысьте, если ниже 10
pbkdf2_sha256$1000000$…PBKDF2 (число — количество итераций)Проверьте, что не меньше 600 000
32 шестнадцатеричных символаСкорее всего MD5Переносите перехешированием при входе
40 / 64 шестнадцатеричных символаСкорее всего SHA-1 / SHA-256То же, что выше
Строка входа как естьОткрытый текстПереносите в первую очередь. Оцените последствия на случай утечки

Проверьте также следующее:

  • Вводимые пароли не записываются в логи приложения или веб-сервера, отчёты об ошибках и инструменты аналитики
  • Если используете bcrypt, вы решили, как обрабатывать ввод длиннее 72 байт
  • Резервные копии и копии базы данных для тестирования содержат только хеши того же вида

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

В инциденте Takufile-bin (2019) несанкционированный доступ к серверу привёл к утечке около 4,8 миллиона записей, а пароли для входа среди них хранились в открытом виде. Если бы они хранились с солью и медленным хешем, риск того, что утёкшие пароли напрямую используют на других сервисах, был бы гораздо ниже. Даже если невозможно предотвратить любое вторжение, способ хранения паролей меняет масштаб ущерба от утечки.

Взгляд этого сайта: для хранения паролей используйте проверенную стандартную реализацию

Хранение паролей — это не место для творчества. Использовать широко проверенные стандартные реализации Argon2/bcrypt — самое безопасное и самое простое в эксплуатации. Наша позиция: не новаторствуйте здесь. Тратьте усилия на снижение зависимости от паролей вообще — на тщательную многофакторную аутентификацию (MFA) и со временем на переход к passkey (без пароля). Даже сильнейший хеш не спасёт слабый, переиспользованный пароль.

Читать дальше

Источники

  • OWASP, «Password Storage Cheat Sheet»: cheatsheetseries.owasp.org
  • NIST, «SP 800-63B-4» (август 2025 г.), хранение паролей (соль и хеширование): pages.nist.gov
  • RFC 9106, «Argon2 Memory-Hard Function for Password Hashing and Proof-of-Work Applications»: rfc-editor.org
  • Руководство PHP, «password_hash»: php.net
  • Документация Django, «Password management in Django» (5.2): docs.djangoproject.com

FAQ

QМожет, просто зашифровать пароли для хранения?
A

Нет — шифрование это неправильный инструмент. Шифрование обратимо при наличии ключа, поэтому, если ключ утечёт, все пароли снова станут открытым текстом. Пароль никогда не нужно читать обратно, даже вам, поэтому необратимый «хеш» — правильный ответ. Но сырого хеша недостаточно; сочетайте соль для каждого пользователя с медленным хешем (bcrypt/Argon2).

Qbcrypt или Argon2 — что использовать?
A

Для новой разработки Argon2 (особенно Argon2id) — первый выбор: он позволяет настраивать и память, и вычисления, что хорошо сопротивляется перебору на GPU. Если вы цените давно существующую, хорошо проверенную реализацию, bcrypt по-прежнему вполне практичен. Ключевой момент с любым из них — использовать реализацию из стандартной библиотеки как есть и не изобретать своё.

QЯ уже храню пароли в MD5/SHA-256. Как перенести?
A

Массово обратно в открытый текст не преобразовать (в этом и смысл), поэтому переносите при входе. В тот миг, когда пользователь успешно входит, перехешируйте правильный пароль, который он только что ввёл, новой схемой (например, Argon2id) и сохраните. Тех, кто не входил, можно пока усилить, повторно захешировав существующий хеш с помощью bcrypt (слоёный хеш).

QЧем соль отличается от перца (pepper)?
A

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

QЧто такое ограничение bcrypt в 72 байта?
A

bcrypt использует только первые 72 байта входных данных; всё после 72-го байта игнорируется. Многобайтовые символы (японский текст, эмодзи) занимают около 3–4 байт каждый, поэтому предел наступает раньше, чем кажется. Для новых систем выбирайте Argon2id, у которого такого ограничения нет; если используете bcrypt, задайте максимальную длину ввода (OWASP).

QНужно ли держать соль в секрете?
A

Нет. Задача соли — сделать так, чтобы один и тот же пароль хешировался по-разному у каждого пользователя, что побеждает предвычисленные таблицы и массовый взлом. Держать её в секрете не нужно, а bcrypt и Argon2 хранят её внутри строки хеша. NIST SP 800-63B-4 требует соль длиной не менее 32 бит.