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

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


Строка bcrypt всегда состоит из нескольких частей:

$2a$12$....................................................

или

$2b$12$....................................................

или

$2y$12$....................................................

Где:

  • $2a$ / $2b$ / $2y$ — версия алгоритма
  • 12 — cost factor (число раундов)
  • далее — соль и хешированное значение

Ключевая особенность: итоговая длина bcrypt-строки всегда составляет 60 символов.

Это константа алгоритма, не зависящая от длины исходного пароля.


Особенности хранения bcrypt.js хешей

Библиотека bcrypt.js в Node.js возвращает строку в стандартном формате crypt, полностью совместимом с оригинальной реализацией bcrypt. Это означает:

  • длина результата всегда 60 символов
  • используется ASCII-безопасная строка
  • нет бинарных данных, требующих BLOB
  • строка полностью самодостаточна (включает соль)

Выбор типа поля в базе данных

CHAR(60)

Наиболее строгий и технически корректный вариант:

password_hash CHAR(60) NOT NULL

Особенности:

  • фиксированная длина
  • отсутствие перерасхода памяти на переменную длину
  • предсказуемость хранения

Используется в случаях, когда структура данных строго контролируется.


VARCHAR(60)

Наиболее распространённый вариант:

password_hash VARCHAR(60) NOT NULL

Особенности:

  • допускает переменную длину (хотя фактически всегда 60)
  • проще переносится между разными СУБД
  • стандарт де-факто в большинстве приложений

Недостаток — небольшие накладные расходы на хранение длины строки.


VARCHAR(255)

Иногда используется как универсальное решение:

password_hash VARCHAR(255) NOT NULL

Причины применения:

  • запас под возможные изменения алгоритмов
  • унификация с другими типами хешей (Argon2, PBKDF2)

Недостаток:

  • избыточное выделение памяти
  • отсутствие строгой схемной дисциплины

TEXT

password_hash TEXT NOT NULL

Используется редко и обычно считается избыточным:

  • отсутствует ограничение длины
  • хуже оптимизация индексов
  • менее строгая схема данных

Рекомендованные ограничения и индексация

Хранение bcrypt-хеша требует учитывать, что:

  • хеш используется только для сравнения
  • индексирование обычно не требуется
  • сравнение выполняется через bcrypt.compare(), а не SQL

Тем не менее, в некоторых системах поле может быть помечено уникальным (например, если используется как идентификатор восстановления):

CREATE UNIQUE INDEX idx_password_hash ON users(password_hash);

Однако такая практика встречается редко и обычно не имеет смысла.


Кодировка и сравнение строк

bcrypt-строка содержит только символы:

  • латиница
  • цифры
  • символы . / $

Это делает её безопасной для:

  • UTF-8
  • ASCII
  • большинства SQL-диалектов без экранирования

Критично избегать:

  • автоматической обрезки строки (truncation)
  • неверных collation (например, case-insensitive сравнение может быть допустимо, но не влияет на bcrypt)

Типовые ошибки при проектировании схемы

Усечение строки

Если поле задано меньше 60 символов:

password_hash VARCHAR(50)

результат:

  • хеш повреждается
  • сравнение паролей становится невозможным
  • восстановление данных неосуществимо

Неправильное предположение о длине

Иногда предполагается, что длина зависит от:

  • сложности пароля
  • количества раундов
  • длины соли

Фактически bcrypt всегда нормализует выход до 60 символов.


Использование неподходящих типов (BLOB / JSON)

bcrypt не требует:

  • бинарного хранения
  • структурированных форматов

Любая попытка хранить его как JSON или BLOB усложняет систему без пользы.


Практическая схема таблицы пользователей

CRE ATE   TABLE users (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    email VARCHAR(255) NOT NULL UNIQUE,
    password_hash CHAR(60) NOT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

Альтернативный вариант:

password_hash VARCHAR(60) NOT NULL

Оба варианта эквивалентны при условии корректной длины.


Поведение bcrypt.js при генерации

В Node.js с использованием bcrypt.js:

const bcrypt = require('bcryptjs');

const hash = bcrypt.hashSync('password123', 12);

результат:

  • строка длиной 60 символов
  • включает соль и cost factor
  • полностью готова к хранению без дополнительной обработки

Масштабирование и совместимость

При переходе на другие алгоритмы хеширования (Argon2, scrypt):

  • длина хеша может значительно увеличиться
  • VARCHAR(60) становится ограничением
  • VARCHAR(255) или TEXT используется как универсальный контейнер

Однако при фиксированном использовании bcrypt.js строгая длина 60 остаётся стабильной характеристикой и позволяет оптимизировать схему хранения данных.