Перемещение старых паролей в новый алгоритм хэширования?
Я переключаю сайт на рельсы. Это довольно большой сайт с 50k + пользователями. Проблема в том, что существующий метод хэширования пароля крайне слаб. У меня есть два варианта:
1) Переключитесь на новый алгоритм, создайте случайные пароли для всех, а затем отправьте по электронной почте эти пароли и потребуйте изменения сразу после
2) Внедрите новый алгоритм, но используйте старый, а затем хэш-результат. Например:
Пароль: abcdef = Алгоритм 1 = > xj31ndn = Алгоритм 2 = > $21aafadsada214
Любые новые пароли должны будут пройти через исходный алгоритм (md5), а затем получить результат этого хеширования, если это имеет смысл? Есть ли недостаток в этом?
Ответы
Ответ 1
Обычно для паролей не требуется reset паролей, можно просто подождать, пока пользователь не войдет в систему в следующий раз.
- Сначала попробуйте проверить введенный пароль с помощью нового алгоритма. Новые пароли и уже преобразованные пароли не будут занимать больше времени для проверки.
- Если это не соответствует, сравните его со старым алгоритмом хеширования.
- Если значение старого хэша соответствует, то вы можете рассчитать и сохранить новый хэш, так как вы знаете пароль.
Каждая система хранения паролей должна иметь возможность переключиться на более эффективный алгоритм хеширования, ваша проблема не является проблемой одноразового переноса. Хорошие алгоритмы хэша паролей, такие как BCrypt, имеют фактор затрат, время от времени вам приходится увеличивать этот коэффициент затрат (из-за более быстрого оборудования), тогда вам нужна такая же процедура, какая вам требуется для миграции.
Ваш вариант 2 с хэшированием старого хеша - это хорошо, если ваш первый алгоритм действительно слаб, и вы хотите немедленно получить дополнительную защиту. В этом случае вы можете вычислить double-hash и заменить старый хеш в базе данных новым двойным хешем.
$newHashToStoreInTheDb = new_hash($oldHashFromDb)
Вы также должны отметить этот пароль-хеш (см. почему), чтобы вы могли распознать его как двойной хэш. Это можно сделать в отдельном поле базы данных, или вы можете включить свою собственную подпись. Современные функции хеша пароля также включают подпись алгоритма, чтобы они могли обновляться до более новых алгоритмов и все еще могут проверять старые хэши. В этом примере показана подпись хэша BCrypt:
$2y$10$nOUIs5kJ7naTuTFkBy1veuK0kSxUFXfuaOKdOKf9xYT0KKIGSJwFa
___
|
signature of hash-algorithm = 2y = BCrypt
Проверка будет выполняться следующим образом:
- Определите, является ли это двойной хэш.
- Если это новый хеш, вызовите новую хеш-функцию, чтобы проверить введенный пароль, и все готово.
- Если это двойной хэш, сравните его с алгоритмом двойного хэша
new_hash(old_hash($password))
.
- Если значение double-hash соответствует, вы можете вычислить и сохранить новый хэш.
Ответ 2
Простейшим решением, вероятно, является добавление в базу данных столбца "Хэш пароля". Сначала установите его на "старый"; когда пользователь входит в систему, повторно введите пароль с помощью нового алгоритма и установите тип базы данных "новым".
Вариант этого метода состоит в том, чтобы сохранить хэш-тип как часть хеш-строки. Это работает так же хорошо, если вы можете однозначно различать разные форматы хешей и имеет то преимущество, что вы можете также включать любые другие необходимые параметры (такие как salt и коэффициент работы для растяжения ключа) в той же строке без необходимости добавлять дополнительные поля для каждого в вашу базу данных.
Например, это подход, обычно используемый современными реализациями склепа (3) Unix (и соответствующие функции на разных языках высокого уровня, такие как PHP): классический DES-основанный (и ужасно слабый) хэш пароля будет выглядеть примерно как abJnggxhB/yWI
, тогда как (слегка) более современный хэш может выглядеть как $1$z75qouSC$nNVPAk1FTd0yVd62S3sjR1
, где 1
задан метод хэширования, z75qouSC
- это соль и nNVPAk1FTd0yVd62S3sjR1
фактический хеш, а разделитель $
выбран, потому что он не может отображаться в хэш-стиле DES старого стиля.
Метод, который вы предлагаете, где новые хэши рассчитываются как:
hash = new_hash( old_hash( password ) )
может быть полезен в некоторых случаях, поскольку он позволяет обновлять все существующие записи, не дожидаясь, пока пользователи войдут в систему. Однако это безопасно только в том случае, если старая хэш-функция сохраняет достаточно энтропии в паролях.
Например, даже довольно старая и слабая криптографическая хеш-функция, такая как несоленый MD5, будет достаточно хорошей, поскольку ее выход зависит от всего ввода и имеет до 128 бит энтропии, что больше, чем почти любой пароль будет иметь (и более чем достаточно, чтобы противостоять атаке грубой силы). С другой стороны, попытка применить эту конструкцию с использованием старой функции crypt (3) на основе DES, поскольку старый хэш будет катастрофическим, поскольку старый склеп (3) игнорирует все, кроме первых 8 символов каждого пароля (а также самые значащие биты даже этих символов).
Ответ 3
Вы можете создать новое поле пароля со всеми пользователями, которые обновили свой пароль с помощью нового метода пароля, и просто обновить всех с помощью вашего варианта 2.
В сочетании с принудительным обновлением пароля при входе для всех пользователей со старым паролем метод автоматически переводит всех активных пользователей на новый метод пароля.
Ответ 4
Альтернативой может быть сохранение обеих хэшей для фазы миграции в отдельных столбцах базы данных:
- Если новый вход не существует во время входа в систему, проверьте его со старым хешем и сохраните новый хэш и удалите старый хэш.
- Если новый хэш существует, используйте только это, чтобы проверить.
Таким образом, через некоторое время вы останетесь только с новыми хэшами - по крайней мере, для тех пользователей, которые вошли в систему хотя бы один раз.