Корректное хранение bcrypt-хешей в базе данных является критически важной частью безопасности аутентификации. Ошибки на этом этапе приводят не только к невозможности входа пользователей, но и к уязвимостям, связанным с подменой или повреждением хешей, а также к непредсказуемому поведению системы авторизации.
Хеш, создаваемый bcrypt.js, представляет собой строку фиксированного формата:
$2b$12$EIXCh9z8q8cQq4w2Jw0Q1uQpQYQ1p9XcQvQk9cWZk1l9cQ2h9xG5K
Он включает:
$2b$)12)Ключевое свойство: строка bcrypt всегда имеет длину около 60 символов.
Ошибки часто возникают при выборе неподходящего типа поля:
VARCHAR(255) — безопасный стандартCHAR(60) — допустим, но менее гибкийTEXT — технически допустим, но избыточенНа практике критически важно:
Наиболее распространённая проблема возникает при ограничении длины поля меньше 60 символов. В этом случае bcrypt-хеш записывается частично:
$2b$12$EIXCh9z8q8cQq4w2Jw0Q1uQpQYQ1p9XcQvQk9cWZk1l9cQ2h9xG5
Даже один потерянный символ делает проверку невозможной.
Некоторые ORM могут:
Особенно критично при использовании:
bcrypt-хеши должны храниться как ASCII-совместимые строки. Любое вмешательство в кодировку приводит к:
$Проверка хранения выполняется не через криптографическую валидацию, а через структурную и функциональную проверку.
Базовые критерии:
$2b$, $2a$ или
$2y$Пример проверки:
function isValidBcryptHash(hash) {
return typeof hash === 'string'
&& /^\$2[aby]\$\d{2}\$/.test(hash)
&& hash.length === 60;
}
Единственный достоверный способ проверки — попытка сравнения:
import bcrypt from 'bcryptjs';
const isValid = await bcrypt.compare('test_password', storedHash);
Если хеш повреждён, результат:
falseПеред записью необходимо убедиться, что хеш корректно сформирован:
const hash = await bcrypt.hash(password, 12);
if (!isValidBcryptHash(hash)) {
throw new Error('Некорректный bcrypt-хеш');
}
await db.users.insert({ password_hash: hash });
При проектировании схемы базы данных важно учитывать:
Пример SQL-схемы:
password_hash VARCHAR(255) NOT NULL
При аудите базы данных проверка выполняется массово:
for (const user of users) {
if (!isValidBcryptHash(user.password_hash)) {
console.log('Повреждённый хеш:', user.id);
}
}
Дополнительно применяются тестовые сравнения:
try {
await bcrypt.compare('dummy', user.password_hash);
} catch (e) {
console.log('Ошибка хеша:', user.id);
}
При обнаружении повреждённых значений применяется стратегия:
Пример генерации нового хеша:
const newHash = await bcrypt.hash(newPassword, 12);
await db.users.update(userId, { password_hash: newHash });
bcrypt включает идентификатор версии (2a,
2b, 2y). В системах с долгим жизненным циклом
важно учитывать:
2a2bbcrypt.compare(password, hash); // версия определяется автоматически
Хеш хранит параметр сложности (cost), например:
$2b$12$...
При проверке корректности хранения важно учитывать:
Если система увеличивает cost factor, применяется стратегия rehash:
const match = await bcrypt.compare(password, hash);
if (match && bcrypt.getRounds(hash) < 14) {
const newHash = await bcrypt.hash(password, 14);
await db.users.update(id, { password_hash: newHash });
}
Даже при корректной длине возможны скрытые ошибки:
$ при сериализации JSONПризнак таких ошибок — стабильный false при корректном
пароле.
VARCHAR(255)TEXT или VARCHAR(255)Система считается корректной при выполнении условий:
bcrypt.compare всегда работает без исключений при
валидных данныхПоддержание этих условий обеспечивает устойчивость аутентификации и исключает скрытые ошибки хранения, которые проявляются только на этапе проверки пароля.