Bcrypt: правильное хеширование паролей
Почему bcrypt для паролей, salt, cost factor, сравнение с SHA, лучшие практики безопасности.
Введение
Хранение паролей в открытом виде — смертный грех разработчика. Хранить их в виде MD5 или SHA-256 — почти так же плохо: современные GPU перебирают миллиарды хешей в секунду. На сцену выходит bcrypt — алгоритм, специально спроектированный для медленного хеширования паролей. В этой статье разберёмся, чем bcrypt отличается от обычных хеш-функций, что такое salt и cost factor, как правильно его применять и почему в 2025 году он всё ещё актуален. Сгенерировать bcrypt-хеш можно в нашем bcrypt-генераторе.
Почему обычные хеши не подходят
Хеш-функции вроде MD5, SHA-256 и SHA-512 создавались для одной цели — быстро вычислять дайджест больших объёмов данных. Скорость для них — достоинство: хеширование гигабайтного файла за секунду помогает проверять целостность.
Но при хранении паролей скорость превращается в уязвимость. Если атакующий получил базу с SHA-256-хешами, он может запустить перебор по словарю на GPU и за часы восстановить миллионы паролей. И добавление соли не спасает — она лишь не позволяет использовать готовые радужные таблицы, но не мешает прямому перебору.
| Алгоритм | Хешей/сек (RTX 4090) | Годен для паролей |
|---|---|---|
| MD5 | ~50 млрд | Нет |
| SHA-256 | ~15 млрд | Нет |
| SHA-512 | ~5 млрд | Нет |
| bcrypt (cost=12) | ~200 | Да |
| argon2id | ~50 | Да (рекомендуется) |
Что такое bcrypt
Bcrypt — это хеш-функция, разработанная Нильсом Пропсом и Дэвидом Мазьером в 1999 году на основе шифра Blowfish. Главное отличие от SHA — намеренная медлительность. Bcrypt использует настраиваемый «cost factor»: при каждом удвоении этого параметра время вычисления хеша также удваивается.
Стандартный bcrypt-хеш выглядит так:
$2b$12$N9qo8uLOickgx2ZMRZoMy.MrqKMD8j6IvF4pYdqLjq5w7XqjK8K7u
└┬┘ └┬┘ └──────────────┬───────────────┘ └───────────────┬───────────────┘
│ │ │ │
│ │ salt (22 символа) хеш (31 символ)
│ cost factor
версия алгоритмаSalt: защита от радужных таблиц
Salt (соль) — это случайная строка, добавляемая к паролю перед хешированием. Зачем она нужна? Если два пользователя выберут одинаковый пароль «qwerty123», то без соли их хеши совпадут. Атакующий, увидев одинаковые хеши, сразу понимает: пароли одинаковые, и ему достаточно подобрать его один раз.
Соль делает каждый хеш уникальным, даже если пароли совпадают. Bcrypt автоматически генерирует 16-байтную соль и встраивает её в итоговый хеш. Это удобно: при проверке пароля алгоритм сам извлекает соль из строки и использует её для вычисления.
Виды salt-стратегий
- Без соли — нельзя использовать, уязвимо к радужным таблицам.
- Глобальная соль — одна на всех. Защищает от готовых таблиц, но не от атак на конкретного пользователя.
- Пер-пользовательская соль — оптимально. Bcrypt делает именно это.
- Пер-пользовательская соль + pepper — соли хранятся в БД, pepper — в коде или secrets-менеджере. Дополнительно защищает при утечке БД.
Cost factor: настраиваемая медлительность
Cost factor (он же «work factor») — это логарифм по основанию 2 от количества раундов. Cost = 12 означает 2¹² = 4096 раундов. Каждый дополнительный шаг удваивает время вычисления.
| Cost | Раундов | Время хеширования (типичное) |
|---|---|---|
| 8 | 256 | ~5 мс |
| 10 | 1024 | ~25 мс |
| 12 | 4096 | ~100 мс |
| 14 | 16384 | ~400 мс |
| 16 | 65536 | ~1,6 с |
Рекомендация OWASP на 2025 год — устанавливать cost factor так, чтобы хеширование занимало 250–500 мс. Для большинства серверов это значение попадает в диапазон 12–14. Увеличивать выше 16 нецелесообразно — пользователи будут слишком долго ждать входа.
Когда увеличивать cost
Закон Мура работает: процессоры становятся быстрее. Хорошая практика — раз в два года повышать cost на единицу. При этом старые хеши не нужно «мигрировать» вручную: при следующем успешном входе пользователя система пересчитывает хеш с новым cost и заменяет старый.
Как использовать bcrypt
Node.js
const bcrypt = require('bcrypt');
// Хеширование пароля
const password = 'my-secret-password';
const saltRounds = 12;
bcrypt.hash(password, saltRounds, (err, hash) => {
console.log(hash);
// $2b$12$N9qo8uLOickgx2ZMRZoMy.MrqKMD8j6IvF4pYdqLjq5w7XqjK8K7u
});
// Проверка пароля
const storedHash = '$2b$12$N9qo8uLOickgx2ZMRZoMy.MrqKMD8j6IvF4pYdqLjq5w7XqjK8K7u';
bcrypt.compare('my-secret-password', storedHash, (err, result) => {
console.log(result); // true
});Python
import bcrypt
password = b'my-secret-password'
# Хеширование
salt = bcrypt.gensalt(rounds=12)
hashed = bcrypt.hashpw(password, salt)
print(hashed)
# b'$2b$12$...'
# Проверка
if bcrypt.checkpw(password, hashed):
print('Пароль верный')Go
package main
import (
"fmt"
"golang.org/x/crypto/bcrypt"
)
func main() {
password := []byte("my-secret-password")
hashed, _ := bcrypt.GenerateFromPassword(password, 12)
fmt.Println(string(hashed))
err := bcrypt.CompareHashAndPassword(hashed, password)
fmt.Println(err == nil) // true
}Версии bcrypt: $2a$, $2b$, $2y$
Префикс в начале хеша указывает на версию алгоритма:
- $2a$ — оригинальная спецификация (1999).
- $2x$ — патч для бага в реализации crypt_blowfish.
- $2y$ — используется в PHP, идентичен $2a$ по поведению.
- $2b$ — актуальная версия, рекомендуемая в новых проектах.
Все версии совместимы на уровне проверки: хеш, созданный как $2a$, можно проверить с помощью библиотеки, ожидающей $2b$, и наоборот.
Ограничения bcrypt
Bcrypt обрезает пароль до 72 байт. Это историческое ограничение, связанное с размером блока Blowfish. Если пользователь задаст пароль длиной 100 символов, последние 28 символов будут проигнорированы. Чтобы этого избежать, разработчики часто предварительно хешируют пароль через SHA-256, а результат уже передают в bcrypt:
const crypto = require('crypto');
const bcrypt = require('bcrypt');
function hashPassword(password) {
// Предхеширование: 64 hex-символа = 64 байта, вписывается в лимит bcrypt
const preHash = crypto.createHash('sha256').update(password).digest('hex');
return bcrypt.hash(preHash, 12);
}
function verifyPassword(password, storedHash) {
const preHash = crypto.createHash('sha256').update(password).digest('hex');
return bcrypt.compare(preHash, storedHash);
}Альтернатива — перейти на argon2id, у которого нет ограничения по длине и который считается наиболее стойким из современных алгоритмов.
Bcrypt vs argon2 vs scrypt
| Алгоритм | Год | Защита от GPU | Защита от ASIC | Сложность настройки |
|---|---|---|---|---|
| bcrypt | 1999 | Средняя | Слабая | Просто |
| scrypt | 2009 | Высокая | Средняя | Средняя |
| argon2id | 2015 | Высокая | Высокая | Средняя |
Argon2id — победитель конкурса Password Hashing Competition 2015 и текущая рекомендация OWASP. Однако bcrypt остаётся отличным выбором для большинства проектов: он проще в настройке, поддерживается везде и десятилетиями доказывал свою надёжность.
Лучшие практики
- Используйте cost factor не ниже 12 для новых проектов.
- Никогда не храните «свой» алгоритм хеширования. Берите проверенную библиотеку.
- Не пытайтесь оптимизировать bcrypt — его медлительность и есть защита.
- Добавьте pepper — секретный ключ из конфигурации, который не хранится в БД.
- Раз в 2 года повышайте cost factor и мигрируйте хеши при следующем входе.
- Логируйте неудачные попытки входа и используйте rate limiting.
- Предложите пользователям двухфакторную аутентификацию.
Заключение
Bcrypt — проверенный временем алгоритм, специально созданный для хранения паролей. Его медлительность — это не баг, а особенность: чем дольше вычисляется один хеш, тем дороже для атакующего массовый перебор. Соль, cost factor и правильная библиотека — этого набора достаточно, чтобы база утекших хешей осталась бесполезной для злоумышленника. Сгенерировать bcrypt-хеш для проверки можно в нашем bcrypt-генераторе, а стойкий пароль для нового аккаунта — вгенераторе паролей.
Попробуйте эти инструменты
Похожие статьи
Как создать надёжный пароль: генератор паролей
Правила создания паролей, длина, символы, энтропия, менеджеры паролей, двухфакторная аутентификация.
MD5 хеш: что это и безопасно ли использовать
Алгоритм MD5, хеширование, коллизии, почему MD5 не для паролей, где ещё можно использовать.
SHA-256: безопасное хеширование
Алгоритм SHA-256, применение в блокчейне, SSL, цифровые подписи, сравнение с MD5 и SHA-1.
UUID: генерация уникальных идентификаторов
UUID версии 1-5, GUID, использование в базах данных, распределённых системах, генерация.