Версии алгоритма: $2a$, $2b$, $2y$ — различия и совместимость

Общая структура идентификатора bcrypt-хеша

Хеши, создаваемые bcrypt, имеют строго определённый формат:

$2<version>$<cost>$<salt><hash>

Где:

  • 2 — базовый идентификатор алгоритма bcrypt
  • <version> — версия реализации
  • <cost> — фактор сложности (logarithmic cost factor)
  • salt — соль (22 символа в модифицированном base64)
  • hash — итоговый 184-битный результат

Ключевой элемент для понимания совместимости — именно версия алгоритма, которая влияет на обработку входных данных и безопасность вычислений.


Версия 2a

Исторический контекст

Версия $2a$ стала одной из первых широко распространённых реализаций bcrypt. Она базируется на оригинальном Blowfish-деривативе и долгое время считалась стандартом де-факто.

Особенности реализации

  • Использует модифицированную функцию экспансии ключа Blowfish
  • Поддерживает корректную обработку строк с нулевыми байтами
  • Является базовой реализацией, от которой происходили последующие исправления

Известные проблемы

Критическая проблема $2a$ связана с некорректной обработкой Unicode-символов в ранних реализациях некоторых библиотек:

  • При наличии нулевых байтов (\0) возможны неоднозначности
  • Некоторые реализации неправильно интерпретировали UTF-8 входные данные
  • Это приводило к различиям между разными языковыми библиотеками

Версия 2b

Причина появления

Версия $2b$ была введена как исправление дефектов $2a$, обнаруженных в обработке длины входных данных и некоторых краевых случаев при работе с бинарными строками.

Основные изменения

  • Исправлена ошибка переполнения при обработке длины строки
  • Устранены проблемы с безопасностью при работе с определёнными входными значениями
  • Уточнена спецификация обработки входных данных

Важные технические детали

Ключевое исправление связано с обработкой длины:

  • В $2a$ длина строки могла быть интерпретирована некорректно при определённых значениях
  • $2b$ корректно обрабатывает длину до 72 байт как строгий лимит входа
  • Поведение стало полностью детерминированным между реализациями

Практическое значение

На текущий момент $2b$ считается наиболее безопасной и стандартной версией bcrypt, используемой в современных библиотеках, включая bcrypt.js и bcrypt для Node.js.


Версия 2y

Происхождение

Версия $2y$ появилась в результате особенностей реализации bcrypt в некоторых системах (в частности, в старых сборках для PHP и совместимых средах). Она не вводит криптографических изменений алгоритма.

Смысл версии

$2y$ является по сути совместимым идентификатором для обхода проблем интерпретации байтов в определённых языковых средах, где $2a$ обрабатывался некорректно.

Техническая суть

  • Алгоритм идентичен $2a$
  • Изменён только маркер версии
  • Предназначен для различения контекста реализации, а не криптографии

Сравнение версий

Криптографическая основа

Все версии:

  • используют Blowfish key schedule
  • имеют одинаковую структуру соли и хеша
  • поддерживают одинаковый диапазон cost-фактора (обычно 4–31)

Криптографически:

  • $2a$ = базовая реализация
  • $2b$ = исправленная и стандартизированная версия
  • $2y$ = идентичен $2a$ по алгоритму

Совместимость хешей

Версия хеша Может проверяться как 2a 2b 2y
2a да да иногда
2b да да да
2y да да да

На практике большинство современных библиотек:

  • принимают все три версии при проверке
  • генерируют преимущественно $2b$

Поведение в bcrypt.js

В JavaScript-реализации bcrypt.js:

  • генерация хеша по умолчанию использует $2b$
  • проверка (compare) поддерживает $2a$, $2b$, $2y$
  • внутренне алгоритм унифицирован, различия учитываются только на уровне парсинга строки

Причины существования нескольких версий

Разделение на $2a$, $2b$, $2y$ не связано с эволюцией криптографической стойкости. Оно возникло из-за:

  • различий в языковых реализациях (C, PHP, JavaScript)
  • ошибок интерпретации строк и байтов
  • необходимости сохранить обратную совместимость с уже созданными хешами
  • уточнения спецификации bcrypt после обнаружения багов

Влияние на безопасность

С точки зрения криптографии:

  • безопасность определяется cost-фактором, а не версией
  • версии не усиливают и не ослабляют алгоритм напрямую
  • $2b$ устраняет реализационные ошибки, которые могли приводить к некорректным результатам

Ключевой риск старых версий связан не с математикой алгоритма, а с интерпретацией входных данных.


Практическая совместимость в системах

В реальных приложениях:

  • базы данных часто содержат смешанные версии хешей
  • миграция на $2b$ происходит постепенно при пересохранении паролей
  • системы аутентификации обязаны поддерживать обратную совместимость

Типичный сценарий:

  1. старый пользователь имеет $2a$ или $2y$ хеш
  2. при входе выполняется проверка
  3. при успешной аутентификации хеш пересоздаётся в $2b$

Итоговое различие на уровне реализации

  • $2a$ — историческая версия, с известными ограничениями обработки входных данных
  • $2b$ — исправленная и текущая стандартная версия bcrypt
  • $2y$ — совместимый маркер для специфических реализаций, не изменяющий алгоритм

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