Что такое «соль» в безопасности? Объяснение хеширования паролей.
В июне 2012 года злоумышленник выложил на российский форум 117 миллионов записей учетных записей LinkedIn. Список представлял собой сплошную стену из хешей SHA-1 без добавления соли. SHA-1 работает быстро. Без соли, которая разделяла бы идентичные пароли, один и тот же хеш встречался рядом с другим хешем тысячи раз. Примерно за семьдесят два часа исследователи безопасности взломали около девяноста процентов файла. Когда утечка данных всплыла в мае 2016 года в рамках набора данных, содержащего 167 миллионов записей, эти учетные данные годами использовались для кампаний по подбору учетных данных.
Способ защиты, который мог бы превратить это нарушение в незначительную проблему — добавление соли к каждому сохраненному паролю — существовал еще в 1979 году. Он называется «солением». Это не ново, не дорого и не особенно умно. Разница лишь в том, что утечка данных может быть локализованным инцидентом, а может стать проблемой для всех пользователей паролей на протяжении пяти лет.
Большинство современных сбоев в хранении паролей по-прежнему происходит по одной и той же причине: кто-то решил, что "мы используем SHA-256" достаточно. В этой статье рассматривается, что такое "соль", как она работает на практике, от чего она защищает, а от чего нет, и что OWASP рекомендует в качестве значения по умолчанию на 2026 год.
Что такое «соль» в системах безопасности и хешировании паролей?
Если свести тему к одному предложению, то добавление случайных данных к паролю означает добавление случайных данных к паролю перед его хешированием. Случайное значение — соль — генерируется один раз для каждой учетной записи, когда соль генерируется вместе с новым хешем пароля, хранится в открытом виде рядом с полученным хешем и считывается при каждом входе в систему. Это не ключ. Не секрет. Не шифрование. Это публичный модификатор, выполняющий всего одну задачу: гарантировать, что если у двух пользователей одинаковый пароль, они никогда не будут использовать один и тот же сохраненный хешированный пароль.
Хэширование само по себе — односторонний процесс. Пропустив пароль через SHA-256 или Argon2id, вы получите значение фиксированной длины, которое ни один алгоритм не сможет расшифровать. Однако «ни один алгоритм» исключает один простой способ обхода проблемы — поиск хеша в предварительно вычисленном словаре. Добавление соли (соль) закрывает этот путь. Добавление соли к процессу хэширования создает уникальный хэш для каждого пользователя, даже если текст его пароля дословно идентичен. Распространенные пароли, такие как «password» и «qwerty», больше не конфликтуют в базе данных. Добавление соли гарантирует, что утечка одной строки ничего не скажет злоумышленнику о других пользователях.
Три правила отличают пригодную для использования соль от бесполезной. Первое правило: каждый пароль получает свою собственную случайную соль, которая генерируется заново при регистрации или сбросе пароля пользователем. Пароль с солью, используемый для разных учетных записей, едва ли лучше, чем пароль без соли — массовая атака работает одинаково. Второе правило: соль может быть общедоступной. Знание соли само по себе не дает злоумышленнику никаких преимуществ перед базовой хеш-функцией. Поэтому соль хранится в базе данных рядом с хешем, и такое размещение допустимо, даже рекомендуется. Третье правило: значения соли берутся из криптографически защищенного генератора псевдослучайных чисел. Linux предоставляет такой генератор через `getrandom(2)`. Node называет его `crypto.randomBytes`. Стандартная библиотека Python оборачивает его в `secrets.token_bytes`. Некриптографический `Math.random()` непригоден, хотя он встречается в реальном проверенном коде гораздо чаще, чем следовало бы.
Два человека, выбравшие пароль "hunter2", должны получить совершенно разные сохраненные хеши. Это происходит благодаря добавлению соли. Если соль не используется, база данных теряет не только хеши, но и всю информацию о том, кто с кем делится паролем.
Как работает соление паролей?
Этот механизм состоит из четырех этапов, и десятилетний опыт утечек данных из хранилищ паролей показывает, что почти каждая ошибка происходит из-за того, что кто-то пропустил один из них.
Первый шаг выполняется во время регистрации. Приложение запрашивает у генератора псевдослучайных чисел от шестнадцати до тридцати двух случайных байтов. Это и есть соль. В рекомендациях OWASP 2025 года шестнадцать байтов (128 бит) считаются минимальным значением; auth0 и несколько поставщиков менеджеров паролей рекомендуют использовать тридцать два. В стандарте NIST SP 800-63B-4 установлен минимум в четыре байта, но предупреждается, что коллизии, связанные с датой рождения, начинают проявляться примерно у шестидесяти пяти тысяч пользователей при таком размере, поэтому никто на самом деле не использует четыре байта.
На втором этапе соль объединяется с выбранным пользователем паролем. Наиболее распространенный способ — конкатенация, при которой соль размещается до или после строки пароля. В некоторых системах вместо этого используется HMAC, где соль рассматривается как ключ HMAC, что позволяет обойти некоторые неочевидные проблемы, связанные с увеличением длины при обычной конкатенации.
На третьем этапе объединенные входные данные обрабатываются с помощью алгоритма хеширования паролей — того, что в учебниках по криптографии называется функцией вывода ключа, или KDF. Именно на этом этапе большинство команд раньше допускали ошибки, часто прибегая к универсальному алгоритму хеширования и считая задачу выполненной. Обычный SHA-256 сам по себе непригоден для хранения паролей. Потребительский графический процессор в 2026 году обрабатывает более ста миллиардов хешей SHA-256 в секунду, поэтому даже с солью стоимость каждой попытки криптографического хеширования остается незначительной. Добавление соли устраняет векторы атаки с использованием радужных таблиц. Само по себе это не замедляет взлом паролей методом перебора. Правильным инструментом здесь является намеренно медленная, ресурсоемкая функция: Argon2id является предпочтительной функцией по умолчанию по версии OWASP, scrypt и bcrypt допустимы, а PBKDF2 допускает очень большое количество итераций там, где это требуется в соответствии со стандартом FIPS-140.
На четвертом этапе сохраняются и хеш, и соль. Современные библиотеки обрабатывают формат хранения, поэтому вам не нужно вручную управлять сохраненной солью. Вывод bcrypt выглядит так: `$2b$12$9f4c8a7b...kQR8YZpL9` — эта единая строка содержит идентификатор алгоритма, коэффициент стоимости, соль и значение хеша. Argon2id использует аналогичный строковый формат PHC: `$argon2id$v=19$m=19456,t=2,p=1$$`. Вы записываете эту строку в столбец базы данных и можете продолжать работу. Библиотека сделала все необходимое для безопасного хранения паролей: сгенерировала случайные данные для соли, пропустила пароль через медленный алгоритм хеширования и упаковала результат для извлечения.
Процесс авторизации происходит в обратном порядке. Приложение ищет соль пользователя и сохраненный хеш, обрабатывает введенный пароль с помощью того же алгоритма с той же солью и сравнивает результат с сохраненным хешем, используя сравнение за постоянное время. Постоянное время имеет значение: наивный оператор `==` раскрывает информацию о количестве совпавших байтов, что привело к появлению реальных уязвимостей для атак по времени. Используйте `hmac.compare_digest`, `crypto.timingSafeEqual` или эквивалентные функции вашего фреймворка.

Почему важно посолить: проблема радужного стола
Радужные таблицы — это именно та атака, для противодействия которой и была изобретена технология «соли». Представьте себе предварительно вычисленную таблицу соответствия, которая сопоставляет каждый правдоподобный пароль — каждое словарное слово, распространенный вариант, запись из списка утечек — с его хешем SHA-256. Такие таблицы существуют, их продают на форумах для хакеров, и их объем исчисляется терабайтами. Как только злоумышленник получает доступ к дампу базы данных с хешами без соли, поиск хеша в радужной таблице становится запросом к базе данных, а не задачей взлома. В 2012 году LinkedIn поступил именно так: 117 миллионов хешей SHA-1 без соли против радужной таблицы, девяносто процентов из которых были взломаны примерно за три дня.
Если добавить соль для каждого пользователя, радужные таблицы перестанут работать. Злоумышленнику потребуется отдельная предварительно вычисленная таблица для каждого уникального значения соли. При использовании шестнадцатибайтовой случайной соли это означает 117 миллионов различных таблиц для 117 миллионов пользователей — и каждая таблица все равно будет занимать терабайты. Одни только затраты на хранение делают это нецелесообразным. Атака перестает быть актуальной.
В этом и заключается вся задача соли. Она намеренно узконаправлена. Добавление соли не замедляет целенаправленную попытку перебора паролей против конкретного пользователя. Добавление соли не предотвращает подбор учетных данных из-за предыдущей утечки. Добавление соли не поможет, если сам пароль — «123456» — злоумышленник пытается использовать очевидный словарь, и соль пересчитывается для каждой попытки, но это не меняет существенно стоимость каждой попытки.
Что надежно предотвращает добавление соли: радужные таблицы и утечку информации о повторном использовании паролей между пользователями в одной базе данных. Что предотвращает медленное хеширование: стоимость каждой попытки угадать пароль. Что предотвращает использование перца: кража данных только из базы данных. Что предотвращает многофакторная аутентификация: подбор учетных данных. Добавление соли — это один из уровней безопасности в стеке — добавление соли для более сложного взлома паролей не то же самое, что сделать каждый пароль трудноугадываемым по отдельности. Цель добавления соли — создание разного хеша для каждого пользователя; сделать каждую попытку угадать пароль дорогостоящей — это отдельная проблема.
Хэширование против шифрования против добавления соли
Три термина, которые часто путают, особенно в статьях неспециалистов. Они не взаимозаменяемы.
| Шифрование | Хэширование | Засолка | |
|---|---|---|---|
| Обратимый | Да, с ключом. | Нет, так задумано. | Модификатор, не является самостоятельным элементом. |
| Длина выходных данных | Переменная, соответствует входным данным | Фиксированный размер (обычно 256 бит) | Н/Д — изменяет входные данные хеша. |
| Используется для | Конфиденциальность данных | Целостность, хранение паролей | Усиление хешей |
| Требуется ключ | Да, секрет | Нет | Нет, используется случайная соль. |
Шифрование защищает данные, которые вы хотите получить позже. Получатель хранит ключ, применяет его и считывает оригинал. Хэширование — односторонний процесс: как только пароль становится хешем, ни один алгоритм не может его восстановить. «Соление» — это модификатор, который применяется к входным данным хэш-функции; сам по себе он не имеет смысла.
Взлом Adobe в 2013 году — показательный пример. Adobe хранила 153 миллиона записей пользователей, зашифровав пароли с помощью 3DES в режиме ECB под одним ключом, используемым на всем сайте. Этот выбор обернулся тройной ошибкой. Шифрование — неподходящий метод для хранения паролей. В режиме ECB идентичные входные данные выдают идентичные выходные. Повторное использование одного ключа для всей базы данных означало, что расшифровка ключа один раз позволяла расшифровать все 153 миллиона записей. Шнайер назвал это одним из худших взломов паролей в истории. Решением на каждом уровне стал переход на медленный хеш с солью.
Соль против перца: двухуровневая защита
«Перец» — менее известный родственник соли. Концепция: дополнительное секретное значение, применяемое к каждому паролю, обрабатываемому приложением, но хранящееся вне базы данных. Это может быть переменная среды, запись в службе управления ключами или ключ, находящийся в HSM. Соль хранится рядом с хешем в открытом виде. «Перец» — нет.
Модель угроз основана на краже только базы данных. Если злоумышленник крадет только базу данных — посредством SQL-инъекции, неправильно настроенной резервной копии или утечки дампа — у него есть соли и хеши, но нет «перца». Без «перца» он даже не может начать перебор, потому что в каждом хешируемом варианте отсутствует глобальный секретный модификатор.
Типичная реализация сначала запускает `argon2id(salt + password + pepper)` или, что более аккуратно, вычисляет `HMAC(pepper, password + salt)` и передает результат в Argon2id. OWASP рекомендует использовать pepper для систем с высокой ценностью, но признает компромисс: ротация pepper — это операционно сложная задача. Ротация означает перехеширование паролей каждого пользователя при следующем входе в систему, и если pepper когда-либо будет потерян без резервной копии, все учетные записи станут недоступными для проверки. Большинство потребительских приложений пропускают pepper. Банки, государственные учреждения и медицинские учреждения обычно этого не делают.
Современное хеширование паролей в 2026 году
В рекомендациях OWASP по хранению паролей на 2025 год четыре алгоритма ранжированы по степени предпочтения. Соль заложена во все из них — её не нужно генерировать вручную.
| Алгоритм | Минимальные требования OWASP 2025 | Спецификация |
|---|---|---|
| Argon2id (предпочтительный) | m=19 МиБ, t=2, p=1 | RFC 9106 (2021) |
| шифр | N=2^17, r=8, p=1 | RFC 7914 (2016) |
| bcrypt (устаревшая версия) | стоимость ≥ 10; ограничение входного значения 72 байта. | Нильс Провос, 1999 |
| PBKDF2-HMAC-SHA256 | 600 000 итераций | RFC 2898; только для FIPS |
Argon2id — алгоритм, требующий больших затрат памяти, то есть каждая попытка угадать значение обходится не только в циклы ЦП, но и в определенный объем оперативной памяти. Минимальный размер хеша по OWASP в 19 МиБ означает, что злоумышленнику, создающему собственную ASIC-систему, также потребуется большой объем быстрой памяти, а память — самая дорогая часть любой системы. Вариант "id" сочетает в себе Argon2i (устойчивый к побочным каналам) и Argon2d (устойчивый к графическому процессору), запуская Argon2i в первой половине и Argon2d во второй.
scrypt появился раньше Argon2id и работает по тому же принципу, требующему больших объемов памяти. bcrypt еще старше, проще и имеет жесткое ограничение на входные данные в 72 байта, что иногда приводит к сбоям в приложениях, использующих длинные парольные фразы — используйте предварительное хеширование с SHA-256 и кодирование в base64, если вам нужны более длинные входные данные. PBKDF2 — это медленный, но соответствующий стандарту FIPS вариант; OWASP увеличила количество итераций для SHA-256 до 600 000 в 2023 году по сравнению с 310 000 ранее.
В этот список не должны входить: обычный SHA-256, обычный SHA-512, MD5, SHA-1 и любые пользовательские функции, заканчивающиеся на "+ соль". Скорость — вот критерий исключения. SHA-256 был разработан для быстрой работы на стандартном оборудовании. Это прямо противоположно тому, что нужно для хранения паролей.
Распространённые ошибки при внесении изменений в коды, выявляемые в ходе реальных проверок.
В реальных отчетах о нарушениях безопасности постоянно указывается одно и то же небольшое количество ошибок.
Повторное использование одной соли для каждого пользователя сводит на нет весь механизм — проблема радужных таблиц возвращается, только с одним дополнительным шагом. Использование имени пользователя в качестве соли еще хуже, поскольку имена пользователей повторяются в разных системах, а радужные таблицы могут быть предварительно вычислены для популярных имен. Длина соли менее шестнадцати байт начинает допускать коллизии при больших базах пользователей. Генерация соли с помощью некриптографической случайной функции — `Math.random()`, `time(0)` mod something, генератор случайных чисел по умолчанию в языке — дает предсказуемые значения, которые лишают соль ее преимуществ.
Более тонкая ошибка — хранение соли на другом сервере «в целях безопасности». Соль не является секретом. Разделение её между системами увеличивает операционную сложность, не повышая при этом криптографическую стойкость. Храните её в той же строке, что и хеш. Современные PHC-строки делают это за вас.
Самая большая проблема, согласно аудитам 2026 года, связана с хешированием с использованием обычного SHA-256 с солью и ее доставкой. Соль предотвращает появление радужных таблиц. Она никак не влияет на ферму графических процессоров, выполняющую десять миллиардов попыток в секунду против одного аккаунта. Решение заключается не в добавлении второй соли, а в переходе на медленный алгоритм генерации ключей, такой как Argon2id.
И последнее: устаревшие хеши без соли. Миграция обязательна. Стандартный подход заключается в проверке устаревшего хеша при следующем входе в систему, повторном хешировании с использованием современного алгоритма и перезаписи. Для пользователей, которые больше никогда не входят в систему, принудительно выполняется сброс пароля через фиксированный промежуток времени.

Почему одной лишь соли недостаточно
«Соление» — это дополнительный уровень защиты, а не крепость. Оно нейтрализует одну конкретную атаку — предварительно вычисленные таблицы — и ничего не говорит о надежности исходного пароля. Метод перебора по-прежнему эффективен против слабых паролей. Подбор учетных данных по-прежнему эффективен против повторно используемых паролей. Фишинг по-прежнему эффективен против любого пароля.
В 2026 году реальная защита паролей включает в себя четыре уровня защиты. Argon2id с шестнадцатибайтовой солью для каждого пользователя обеспечивает хранение данных. «Перец» (необязательный, но полезный для конфиденциальных систем) блокирует кражу данных только из баз данных. Многофакторная аутентификация блокирует подбор учетных данных и большинство фишинговых атак. Непрерывный мониторинг утечек — такие сервисы, как Have I Been Pwned и его API — фиксируют момент утечки учетных данных пользователя, чтобы принудительно сбросить их.
Пропустите любой из этих уровней, и злоумышленник в конце концов найдет уязвимое место. Использование только «соли» или любой другой меры защиты — это именно то, что каждый анализ утечек данных за последнее десятилетие определял как точку отказа.