Соль (salt) в криптографических хэш-функциях — это случайная последовательность данных, которая добавляется к исходному значению перед его хэшированием. Основная задача соли — сделать результат хэширования уникальным даже для одинаковых входных данных, тем самым значительно усложняя атаки, основанные на предварительно вычисленных таблицах (rainbow tables) и словарных подборках.
Атака по словарю строится на предположении, что злоумышленник может заранее перебрать множество популярных паролей, вычислить их хэши и сравнивать их с украденной базой данных. Если хэш не защищён дополнительными механизмами, одинаковые пароли всегда дают одинаковый результат, что делает такие атаки чрезвычайно эффективными.
Особенно уязвимы системы, где:
Соль нарушает ключевое предположение атакующего: повторяемость результата.
Соль — это случайная строка, которая добавляется к паролю до выполнения хэш-функции:
hash = H(password + salt)
или в более безопасной форме:
hash = H(salt + password)
Главная цель заключается не в сокрытии пароля, а в том, чтобы сделать каждый хэш уникальным, даже если входные данные одинаковы у разных пользователей.
Без соли злоумышленник может один раз вычислить хэш для списка популярных паролей:
После этого он просто сверяет значения с базой данных.
С использованием соли ситуация меняется:
user1: password + A1B2C3 → hash1
user2: password + X9Y8Z7 → hash2
Даже одинаковый пароль даёт разные хэши. Это делает невозможным использование заранее подготовленных таблиц.
Библиотека CryptoJS позволяет реализовать хэширование с солью вручную, поскольку базовые функции хэширования не включают встроенный механизм генерации соли.
Пример использования SHA-256 с солью:
import CryptoJS from "crypto-js";
const password = "mySecurePassword";
const salt = CryptoJS.lib.WordArray.random(16).toString();
const hash = CryptoJS.SHA256(password + salt).toString();
console.log("Salt:", salt);
console.log("Hash:", hash);
Здесь используется случайная соль длиной 16 байт. Каждый раз при генерации она будет отличаться, что гарантирует уникальность результата.
Соль не является секретом. Её необходимо хранить вместе с хэшом, поскольку она потребуется при проверке пароля.
Типичная структура записи:
{
passwordHash: "...",
salt: "..."
}
При проверке пароля:
const inputPassword = "userInput";
const storedSalt = db.salt;
const storedHash = db.passwordHash;
const checkHash = CryptoJS.SHA256(inputPassword + storedSalt).toString();
const isValid = checkHash === storedHash;
На практике встречаются типичные ошибки, которые полностью сводят защиту на нет:
Если соль фиксированная:
const salt = "staticSalt";
то атака по словарю снова становится возможной, поскольку предвычисления сохраняют свою эффективность.
Соль должна быть случайной и достаточно длинной. Использование коротких строк вроде “123” или “salt” делает систему уязвимой.
Каждый пользователь должен иметь собственную соль. Повторное использование снижает уровень защиты до минимального.
Rainbow tables строятся на принципе предварительного вычисления большого количества хэшей для популярных паролей. Соль ломает этот механизм, потому что:
Даже небольшое увеличение длины соли делает построение таких таблиц практически нецелесообразным.
Соль часто используется вместе с более сложными алгоритмами:
CryptoJS поддерживает PBKDF2, который уже включает механизм соли и многократных итераций:
const key = CryptoJS.PBKDF2(password, salt, {
keySize: 256 / 32,
iterations: 10000
}).toString();
В этом случае соль не просто добавляется, а участвует в процессе генерации ключа вместе с итерациями, что существенно замедляет перебор.
Качественная соль должна удовлетворять следующим условиям:
CryptoJS обеспечивает генерацию через
CryptoJS.lib.WordArray.random, что подходит для большинства
прикладных задач.
Добавление соли не делает пароль «не взламываемым», но резко увеличивает стоимость атаки. Без соли злоумышленник может использовать заранее подготовленные базы данных. С солью он вынужден атаковать каждую запись отдельно, что делает массовые атаки значительно менее эффективными.
С точки зрения криптографической устойчивости соль — это базовый обязательный элемент, без которого любая схема хранения паролей считается устаревшей.