Хэш пароля, формируемый библиотеками уровня Password-hash, представляет собой не просто результат криптографической функции, а структурированную строку, в которой закодированы параметры алгоритма, соль и итоговое значение. Такая композиция обеспечивает воспроизводимость проверки и устойчивость к подбору при условии корректной конфигурации параметров.
Большинство реализаций используют строковую сериализацию следующего логического вида:
algorithm$parameters$salt$hash
или более компактные варианты:
algorithm$iterations$salt$hash
algorithm$cost$salt$hash
Разделителем почти всегда выступает символ $, поскольку
он редко встречается в base64- или hex-кодировке и упрощает парсинг без
дополнительной экранизации.
Первая часть строки отвечает за выбор криптографического примитива. Она позволяет системе однозначно определить, каким способом был получен хэш.
Типичные варианты:
sha1sha256bcryptpbkdf2argon2 (в более современных реализациях)Пример:
sha256$...
Эта часть критична при миграции между алгоритмами: при изменении схемы хранения система может одновременно поддерживать несколько типов хэшей.
Следующий сегмент содержит настройки сложности вычисления хэша. Их структура зависит от конкретного алгоритма.
Используются в PBKDF2 и схожих функциях:
pbkdf2$10000$...
Число итераций напрямую влияет на стоимость вычисления: чем выше значение, тем медленнее подбор пароля.
В алгоритмах типа bcrypt применяется экспоненциальный параметр:
bcrypt$12$...
Значение 12 означает 2^12 раундов преобразований.
Соль — случайная строка, добавляемая к паролю перед хэшированием. Она предотвращает использование радужных таблиц и делает одинаковые пароли различными на уровне результата.
Формат соли зависит от кодировки:
Пример:
sha256$10000$k9d2Hf8kLqP1$...
Соль должна быть уникальной для каждого пароля и сохраняется внутри итоговой строки, поскольку требуется при проверке.
Последняя часть — итоговое значение после применения алгоритма.
Пример:
sha256$10000$k9d2Hf8kLqP1$e4b7c9a1f0d8...
Хэш обычно кодируется в base64 или hex. Его длина фиксирована и зависит от используемой функции:
Логика построения итоговой строки включает несколько этапов:
Псевдологика:
salt = random()
hash = KDF(password + salt, params)
result = algorithm + "$" + params + "$" + salt + "$" + encode(hash)
Каждый компонент строки проходит нормализацию перед объединением.
Используется для компактного представления бинарных данных. Внутри могут присутствовать символы:
A–Za–z0–9+ /Иногда заменяются на URL-safe вариант:
- _Представляет байты в виде шестнадцатеричных пар:
e4b7c9a1f0d8...
Некоторые реализации добавляют версию алгоритма в начало строки:
$2b$12$salt$hash
или:
v1$sha256$10000$salt$hash
Это позволяет:
Символ $ используется как структурный маркер. Он
разделяет логические блоки:
Преимущество такого подхода заключается в отсутствии необходимости хранить метаданные отдельно в базе данных — вся информация инкапсулируется в одной строке.
Объединение соли и результата в одну строку даёт несколько технических преимуществ:
При проверке пароля строка разбирается обратно на компоненты, после чего выполняется повторное вычисление хэша с извлечённой солью.
Алгоритм проверки включает обратный процесс:
$Псевдоструктура:
[algorithm, params, salt, storedHash] = parse(hashString)
computedHash = KDF(inputPassword, salt, params)
compare(storedHash, computedHash)
Финальный этап сравнения выполняется с защитой от timing-атак. Обычное сравнение строк недопустимо, поскольку может раскрыть информацию о совпадающих префиксах.
Используется побайтовое сравнение с фиксированным временем выполнения независимо от результата.
Строка хэша в формате Password-hash является самодостаточной структурой:
Это позволяет системе не хранить дополнительные метаданные и при этом корректно восстанавливать процесс проверки даже спустя годы после генерации.