Соль и её роль в защите от атак по словарю

Соль (salt) в криптографических хэш-функциях — это случайная последовательность данных, которая добавляется к исходному значению перед его хэшированием. Основная задача соли — сделать результат хэширования уникальным даже для одинаковых входных данных, тем самым значительно усложняя атаки, основанные на предварительно вычисленных таблицах (rainbow tables) и словарных подборках.

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

Особенно уязвимы системы, где:

  • используется быстрый хэш без модификаций;
  • отсутствует индивидуальная модификация входных данных;
  • пароли короткие или предсказуемые;
  • применяется одинаковый алгоритм без дополнительных параметров.

Соль нарушает ключевое предположение атакующего: повторяемость результата.

Суть соли в хэшировании

Соль — это случайная строка, которая добавляется к паролю до выполнения хэш-функции:

hash = H(password + salt)

или в более безопасной форме:

hash = H(salt + password)

Главная цель заключается не в сокрытии пароля, а в том, чтобы сделать каждый хэш уникальным, даже если входные данные одинаковы у разных пользователей.

Почему соль эффективна против словарных атак

Без соли злоумышленник может один раз вычислить хэш для списка популярных паролей:

  • password → 5f4dcc3b5aa765d61d8327deb882cf99
  • 123456 → e10adc3949ba59abbe56e057f20f883e

После этого он просто сверяет значения с базой данных.

С использованием соли ситуация меняется:

user1: password + A1B2C3 → hash1
user2: password + X9Y8Z7 → hash2

Даже одинаковый пароль даёт разные хэши. Это делает невозможным использование заранее подготовленных таблиц.

Использование соли в CryptoJS

Библиотека 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;

Ошибки при использовании соли

На практике встречаются типичные ошибки, которые полностью сводят защиту на нет:

1. Использование одной соли для всех пользователей

Если соль фиксированная:

const salt = "staticSalt";

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

2. Слишком короткая или предсказуемая соль

Соль должна быть случайной и достаточно длинной. Использование коротких строк вроде “123” или “salt” делает систему уязвимой.

3. Отсутствие уникальности

Каждый пользователь должен иметь собственную соль. Повторное использование снижает уровень защиты до минимального.

Связь соли с радужными таблицами

Rainbow tables строятся на принципе предварительного вычисления большого количества хэшей для популярных паролей. Соль ломает этот механизм, потому что:

  • один и тот же пароль даёт разные хэши;
  • таблицу невозможно универсально применить ко всем пользователям;
  • объём необходимых вычислений становится экспоненциальным.

Даже небольшое увеличение длины соли делает построение таких таблиц практически нецелесообразным.

Соль и производные механизмы защиты

Соль часто используется вместе с более сложными алгоритмами:

  • PBKDF2
  • bcrypt
  • scrypt
  • Argon2

CryptoJS поддерживает PBKDF2, который уже включает механизм соли и многократных итераций:

const key = CryptoJS.PBKDF2(password, salt, {
  keySize: 256 / 32,
  iterations: 10000
}).toString();

В этом случае соль не просто добавляется, а участвует в процессе генерации ключа вместе с итерациями, что существенно замедляет перебор.

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

Качественная соль должна удовлетворять следующим условиям:

  • случайность (использование криптографически стойкого генератора);
  • уникальность для каждого значения;
  • достаточная длина (обычно от 16 байт и выше);
  • отсутствие зависимости от входных данных.

CryptoJS обеспечивает генерацию через CryptoJS.lib.WordArray.random, что подходит для большинства прикладных задач.

Практическое влияние на безопасность

Добавление соли не делает пароль «не взламываемым», но резко увеличивает стоимость атаки. Без соли злоумышленник может использовать заранее подготовленные базы данных. С солью он вынужден атаковать каждую запись отдельно, что делает массовые атаки значительно менее эффективными.

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