По солям:
Соль - это общедоступные данные (если она должна была быть конфиденциальной, то она не была бы названа "солью", а "ключом"). Соль должна быть известна для проверки пароля (поскольку она входит в хэш-функцию вместе с паролем), поэтому она сохраняется в виде открытого текста, как вы и собираетесь делать.
Смысл в том, чтобы не дать злоумышленнику разделить затраты на атаку между экземплярами атаки. Угадать выбранный пользователем пароль часто возможно (пользователи имеют только человеческий мозг и редко умеют запоминать сложные пароли). Соль гарантирует, что, по крайней мере, злоумышленник должен будет заплатить полную цену угадывания пароля (то есть "пробовать" большой словарь возможных паролей) за каждый атакованный пароль.
Соль работает хорошо, пока она уникальна для каждого пароля. Обратите внимание, что уникального для каждого пользователя недостаточно: иногда пользователи меняют свои пароли, и вы не хотите использовать ту же соль для следующего пароля (в противном случае некоторый уровень разделения затрат становится доступным для атакующий). Таким образом, вы хотите выбрать новую соль при каждом сохранении пароля, то есть при создании учетной записи и при каждом изменении пароля.
Уникальность солей легко обеспечивается использованием достаточно больших случайных солей. Для этого более чем 32-байтовых солей достаточно: риск выбора дважды одной и той же соли (из-за неудачи) ничтожен.
Дополнительный уровень безопасности, помимо засолки, состоит в том, чтобы сделать хеширование пароля "дорогим". Например, когда вы хэшируете (соленый) пароль, вы фактически хешируете конкатенацию в 10000 раз больше последовательности соль + пароль. Это делает проверку пароля в 10000 раз дороже, как для вас, так и для злоумышленника. Часто бывает так, что законная проверка пароля может быть более сложной в вычислительном отношении без заметного эффекта (даже если вы проверяете 100 пользовательских паролей в секунду, вы все равно можете посвятить этому 100 мкс, и она будет использовать только 1% вашего времени обработки), но сделать атакующему в 10000 раз тяжелее - хорошая идея.
На языках:
Основная проблема с паролями, отличными от ASCII, заключается в том, что некоторые глифы могут использовать несколько кодировок. Например, 'é' может быть закодировано с помощью Unicode в виде одной кодовой точки (U + 00E9 МАЛЕНЬКОЕ ПИСЬМО E С ОСТРОМ) или в виде двух кодовых точек (U + 0065 ЛАТИНСКОЕ МАЛЕНЬКОЕ ПИСЬМО E, за которым следует U + 0301 КОМБИНИРОВАНИЕ ОСТРЫЙ АКЦЕНТ). Обратите внимание, что в этом примере речь идет о довольно распространенном французском письме, ничем не более таком (компьютерном), как китайское или корейское. Проблемы с кодированием можно решить с помощью форм нормализации Unicode , но это не простая задача программирования. Могут помочь некоторые среды программирования (например, в Java используйте java.text.Normalizer
, который реализует UNF).
Кроме того, пароли, не входящие в ASCII, могут оказаться сложными для ввода пользователями, которые переключают клавиатуры, например когда пользователь хочет использовать общий компьютер в отеле или аэропорту (в любом случае ввод конфиденциальных паролей в таких системах не очень хорошая идея).
Я бы порекомендовал попытаться применить пароли только к ASCII, если это возможно, или иным образом прикусить пулю: выполнить UNF (с формой NFD), затем кодировать UTF-8. В результате получается последовательность байтов, которая переходит на этап хеширования.