Хорошо, давайте пройдемся по нескольким пунктам здесь
То, что у вас есть в $salt
, не является солью.Это детерминистично (это означает, что там вообще нет случайности).Если вы хотите соль, используйте либо mcrypt_create_iv($size, MCRYPT_DEV_URANDOM)
, либо другой источник фактической случайной энтропии.Дело в том, что он должен быть как уникальным, так и случайным.Обратите внимание, что он не должен быть криптографически безопасным случайным образом ... В худшем случае я бы сделал что-то вроде этого:
function getRandomBytes($length) {
$bytes = '';
for ($i = 0; $i < $length; $i++) {
$bytes .= chr(mt_rand(0, 255));
}
return $bytes;
}
Как указал @ Anony-Mousse, никогда передавать выходные данные одной хэш-функции в другую без повторного добавления к ней исходных данных.Вместо этого используйте правильный итеративный алгоритм, такой как PBKDF2 , PHPASS или CRYPT_BLOWFISH ($ 2a $).
Мое предложение будет использоватьcrypt
с blowfish, так как это лучшее из доступных для PHP на данный момент:
function createBlowfishHash($password) {
$salt = to64(getRandomBytes(16));
$salt = '$2a$10$' . $salt;
$result = crypt($password, $salt);
}
А затем проверьте, используя такой метод:
function verifyBlowfishHash($password, $hash) {
return $hash == crypt($password, $hash);
}
(обратите внимание, что to64
хороший метод, определенный здесь ).Вы также можете использовать str_replace('+', '.', base64_encode($salt));
...
Я бы также предложил вам прочитать следующие два:
Редактировать: Ответить на вопрос о миграции
Хорошо, такЯ понимаю, что мой ответ не касался миграционного аспекта исходного вопроса.Вот как я бы это решил.
Сначала создайте временную функцию для создания нового хэша blowfish из исходного хеша md5 со случайной солью и префиксом, чтобы мы могли обнаружить это позже:
function migrateMD5Password($md5Hash) {
$salt = to64(getRandomBytes(16));
$salt = '$2a$10$' . $salt;
$hash = crypt($md5Hash, $salt);
return '$md5' . $hash;
}
Теперь запустите все существующие хеши md5 через эту функцию и сохраните результат в базе данных.Мы добавили наш собственный префикс, чтобы мы могли определить исходный пароль и добавить дополнительный шаг md5.Итак, теперь мы все перенесены.
Затем создайте еще одну функцию для проверки паролей и при необходимости обновите базу данных новым хешем:
function checkAndMigrateHash($password, $hash) {
if (substr($hash, 0, 4) == '$md5') {
// Migrate!
$hash = substr($hash, 4);
if (!verifyBlowfishHash(md5($password), $hash) {
return false;
}
// valid hash, so let's generate a new one
$newHash = createBlowfishHash($password);
saveUpdatedPasswordHash($newHash);
return true;
} else {
return verifyBlowfishHash($password, $hash);
}
}
Это то, что я бы предложил длянесколько причин:
- Он сразу же получает хэш-данные
md5()
из вашей базы данных. - В конечном итоге (следующий логин для каждого пользователя) обновляет хэш до лучшей альтернативы (одинэто хорошо понятно).
- В коде довольно легко следовать.
Чтобы ответить на комментарии:
Сольне должен быть случайным - я направляю вас к RFC 2898 - Криптография на основе пароля .А именно, раздел 4.1.И я цитирую:
Если нет беспокойства о взаимодействии между многократным использованием одного и того же ключа (или префикса этого ключа) с методами шифрования на основе пароля и аутентификации, поддерживаемыми для данного пароля,тогда соль может генерироваться случайным образом и нет необходимости проверять конкретный формат стороной, получающей соль.Длина должна быть не менее восьми октетов (64 бита).
Дополнительно
Примечание.Если генератор случайных чисел или генератор псевдослучайных данных недоступен, детерминированная альтернатива для генерации соли (или ее случайной части) заключается в применении к паролю функции вывода ключа на основе пароля и обрабатываемого сообщения М.
Доступен генератор псевдослучайных данных, так почему бы не использовать его?
Ваше решение такое же, как bcrypt?Я не могу найти много документации о том, что на самом деле представляет собой bcrypt? - я предполагаю, что вы уже прочитали статью Википедии bcrypt и попытаетесь объяснить ее лучше.
BCrypt основан на блочном шифре Blowfish.Он берет алгоритм настройки расписания ключей из шифра и использует его для хеширования паролей.Причина, по которой он хорош, заключается в том, что алгоритм настройки Blowfish разработан очень дорогим (что является частью того, что делает blowfish таким сильным из шифров).Базовый процесс выглядит следующим образом:
Массив из 18 элементов (называемых блоками P, размером 32 бита) и 4 двумерных массива (называемых блоками S, каждый с 256 записями по 8биты каждый) используются для настройки расписания путем инициализации массивов с заданными статическими значениями.Кроме того, 64-битное состояние инициализируется всеми 0.
Переданный ключ XOred со всеми 18-ю блоками по порядку (поворот ключа, если он слишком короткий).
Затем поля P используются для шифрования ранее инициализированного состояния.
Зашифрованный текст, созданный на шаге 3, используется для замены P1 и P2 (первые 2 элемента массива P).
Шаг 3 повторяется, и результат помещается в P3 и P4.Это продолжается до тех пор, пока не будут заполнены P17 и P18.
Это ключ, полученный из шифра Blowfish.BCrypt изменяет это так:
64-битное состояние инициализируется как зашифрованная версия соли .
То же самое
Затем используются поля P для шифрования (состояние или часть соли ), которое было предварительно инициализировано.
То же самое
То же самое
Полученная настройка затем используется для шифрования пароля 64 раза.Вот что возвращает BCrypt.
Суть проста: это очень дорогой алгоритм, который отнимает много времени процессора.Это реальная причина, по которой его следует использовать.
Надеюсь, это прояснит ситуацию.