Зачем нужна соль (salt) при хранении паролей: разбор с примерами

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


Почему без соли небезопасно

Хеш-функция детерминирована: на одинаковый вход она всегда даёт одинаковый выход. Если хранить просто хеш пароля, возникают две проблемы.

Проблема 1: одинаковые пароли → одинаковые хеши.
Если у 100 пользователей пароль 123456, в базе будет 100 одинаковых хешей. Злоумышленнику достаточно взломать один хеш — и он получит доступ ко всем этим аккаунтам.

Проблема 2: радужные таблицы.
Это заранее подготовленные базы, где для миллионов популярных паролей уже посчитаны хеши. Получив дамп базы, атакующий просто ищет совпадения в такой таблице и быстро восстанавливает пароли.

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


Как это работает на практике

Схема простая:

  1. При регистрации генерируют случайную соль.
  2. Пароль объединяют с солью.
  3. Полученную строку хешируют.
  4. В базу сохраняют соль и хеш (пароль не хранят).
  5. При входе берут сохранённую соль, соединяют с введённым паролем, хешируют и сравнивают с сохранённым хешем.

Пример на PHP (современный подход)

Для хранения паролей не используют «соль вручную» с простыми алгоритмами вроде MD5 или SHA‑256. Вместо этого применяют встроенные функции, которые сами генерируют и используют соль.

php
// Регистрация: создание хеша
$password = 'my_secret_password';
$hash = password_hash($password, PASSWORD_DEFAULT);
// $hash содержит алгоритм, соль и сам хеш в одной строке
// Пример значения $hash: $2y$10$XY12ab... (дальше длинная строка)
 
// Сохраняем в БД только $hash
$stmt = $pdo->prepare('INSERT INTO users (email, password_hash) VALUES (?, ?)');
$stmt->execute([$email, $hash]);
php
// Вход: проверка пароля
$inputPassword = $_POST['password'];
$storedHash = $row['password_hash']; // взяли из БД
 
if (password_verify($inputPassword, $storedHash)) {
    // пароль верный, пускаем пользователя
} else {
    // неверный пароль
}

Здесь соль не передаётся отдельно: password_hash сам создаёт криптографически стойкую соль, а password_verify извлекает её из строки хеша и использует для проверки. Это правильный способ для PHP 7.4 и новее.


Что будет, если делать «соль вручную» неправильно

Частая ошибка — брать одну общую соль для всех пользователей и хранить её в коде.

php
// ПЛОХОЙ ПРИМЕР (не используйте)
$globalSalt = 'fixed_salt_string';
$hash = hash('sha256', $password . $globalSalt);

Минусы такого подхода:

  • У пользователей с одинаковым паролем будут одинаковые хеши.
  • Если злоумышленник узнает общую соль (например, из кода), он сможет подготовить радужную таблицу именно под эту соль и быстро взломать все хеши.

Ещё хуже — вообще не использовать соль:

php
// ОЧЕНЬ ПЛОХО (не используйте)
$hash = hash('sha256', $password);

В этом случае любой популярный пароль мгновенно находится по радужным таблицам.


Важные нюансы

  • Соль не должна быть секретной. Её хранят прямо в базе рядом с хешем. Секретность соли не даёт дополнительной защиты; её ценность — в уникальности для каждого пользователя и достаточной длине.
  • Длина и качество соли. Соль должна генерироваться криптографически стойким генератором случайных чисел. Встроенные функции вроде password_hash делают это правильно.
  • Не изобретайте свои схемы. Для хранения паролей используйте стандартные функции: password_hash/password_verify в PHP, bcrypt/argon2 в других языках. Они уже учитывают соль, количество итераций и другие параметры безопасности.

Ещё один пример: сравнение «без соли» и «с солью»

Допустим, два пользователя выбрали пароль qwerty.

Без соли (хеш SHA‑256):

  • Пользователь 1: hash('sha256', 'qwerty')d8578edf8458ce06fbc5bb76a58c51b3...
  • Пользователь 2: hash('sha256', 'qwerty') → тот же самый хеш.

По хешам видно, что пароли совпадают. Взломав один, атакующий получает оба аккаунта.

С солью (через password_hash):

  • Пользователь 1: хеш будет, например, $2y$10$a1b2c3d4e5f6g7h8i9j0k1...
  • Пользователь 2: хеш будет совсем другим, например $2y$10$z9y8x7w6v5u4t3s2r1q0p9...

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


Практический вывод

  • Соль нужна, чтобы одинаковые пароли давали разные хеши и чтобы сделать радужные таблицы практически бесполезными.
  • В современных проектах не подбирают соль вручную и не склеивают её с паролем в коде. Используют готовые функции типа password_hash.
  • В базе хранят только хеш (в котором уже «зашита» соль) и, при необходимости, отдельно — саму соль как часть формата хеша (но в случае password_hash это единая строка).

Добавить комментарий