Сравнение паролей с помощью crypt() в PHP
Мне нужно получить основы этой функции. Документация php.net утверждает, для алгоритма Blowfish, что:
Хеширование Blowfish с солью выглядит следующим образом: "$2a$", двухзначный параметр стоимости, "$" и 22 64 цифры от алфавита "./0-9A-Za-z". Использование символов вне этого диапазона в соли приведет к тому, что crypt() вернет строку нулевой длины
Так что это, по определению, не должно работать:
echo crypt('rasmuslerdorf', '$2a$07$usesomadasdsadsadsadasdasdasdsadesillystringforsalt$');
Тем не менее, он выплевывает:
$2a$07$usesomadasdsadsadsadaeMTUHlZEItvtV00u0.kb7qhDlC0Kou9e
Где кажется, что crypt() нарезал саму соль на длину 22. Может кто-нибудь объяснить это?
Еще один аспект этой функции, который я не могу понять, - когда они используют crypt() для сравнения паролей. http://php.net/manual/en/function.crypt.php (см. пример #1). Означает ли это, что если я использую одну и ту же соль для шифрования всех своих паролей, мне придется сначала ее зашифровать? то есть:
$salt = "usesomadasdsadsadsadae";
$salt_crypt = crypt($salt);
if (crypt($user_input, $salt) == $password) {
// FAIL WONT WORK
}
if (crypt($user_input, $salt_crypt) == $password) {
// I HAVE TO DO THIS?
}
Спасибо за ваше время
6 ответов
Следующий пример кода может ответить на ваши вопросы.
Чтобы сгенерировать хешированный пароль с помощью Blowfish, сначала нужно сгенерировать соль, которая начинается с $2a$, за которым следует число итераций и 22 символа строки Base64.
$salt = '$2a$07$usesomadasdsadsadsadasdasdasdsadesillystringfors';
$digest = crypt('rasmuslerdorf', $salt);
Сохраните весь $digest в базе данных, в нем есть и соль, и дайджест.
При сравнении пароля, просто сделайте это,
if (crypt($user_input, $digest) == $digest)
Вы повторно используете дайджест как соль. crypt знает, как долго соль из идентификатора алгоритма.
Новая соль для каждого пароля
$password = 'p@ssw0rd';
$salt = uniqid('', true);
$algo = '6'; // CRYPT_SHA512
$rounds = '5042';
$cryptSalt = '$'.$algo.'$rounds='.$rounds.'$'.$salt;
$hashedPassword = crypt($password, $cryptSalt);
// Store complete $hashedPassword in DB
echo "<hr>$password<hr>$algo<hr>$rounds<hr>$cryptSalt<hr>$hashedPassword";
Аутентификация
if (crypt($passwordFromPost, $hashedPasswordInDb) == $hashedPasswordInDb) {
// Authenticated
Цитирование из руководства
CRYPT_BLOWFISH - Хеширование Blowfish с солью выглядит следующим образом: "$2a$", двухзначный параметр стоимости, "$", и 22 базовых 64 цифры из алфавита
Примечание: 22 из 64 цифр
BCrypt использует 128 битов для соли, то есть 22 байта Base64 с использованием только двух битов последнего использованного байта.
Хеш вычисляется с использованием соли и пароля. Когда вы передаете зашифрованный пароль, алгоритм считывает силу, соль (игнорируя все, что находится за его пределами) и пароль, который вы дали, и вычисляет хеш, добавляя его. Если у вас есть PostgreSQL и pg_crypto, SELECT gen_salt('bf'); покажет вам, что из $ соли читается.
Вот пример кода для генерации соли, из test-vector-gen.php моей реализации.NET, альтернативно:
$salt = sprintf('$2a$%02d$%s', [strength goes here],
strtr(str_replace(
'=', '', base64_encode(openssl_random_pseudo_bytes(16))
),
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/',
'./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'));
Нет причин использовать одну и ту же соль для всех ваших паролей. В любом случае, соль является частью вывода, так что вы ничего не получите в удобстве... хотя я допускаю, что PHP должен иметь встроенную функцию gen_salt.
Этот вопрос связан с моим ответом на ответ ZZ Coder. В основном мой вопрос касается хранения результата crypt() в базе данных. Должен ли я хранить весь вывод в базе данных, чтобы моя база данных выглядела так:
--------------------------------------------------------------------------------
| ID | Username | Password |
--------------------------------------------------------------------------------
| 32 | testuser | $2a$07$usesomadasdsadsadsadaeMTUHlZEItvtV00u0.kb7qhDlC0Kou9e |
--------------------------------------------------------------------------------
Если да, то разве это не бросает вызов цели использования соли? Если кто-то получит доступ к БД, он сможет ясно увидеть соль, используемую для шифрования?
Бонусный вопрос: безопасно ли использовать одну и ту же соль для каждого пароля?
Первый вопрос:
Так что это, по определению, не должно работать:
echo crypt('rasmuslerdorf', '$2a$07$usesomadasdsadsadsadasdasdasdsadesillystringforsalt$');
Где кажется, что crypt() нарезал саму соль на длину 22. Может кто-нибудь объяснить это?
Нет проблем с наличием слишком большого количества символов... фраза Использование символов вне этого диапазона в соли приведет к тому, что crypt() вернет строку нулевой длины со ссылкой вне диапазона base 64, а не диапазона 22 персонажи. Попробуйте вставить недопустимый символ в строку соли, и вы обнаружите, что вы получаете пустой вывод (или если вы вставите < 22 символа, что приведет к недопустимым пустым байтам).
Второй вопрос:
Вы передаете зашифрованный хранимый пароль как соль, потому что строка соли всегда появляется (по замыслу) в зашифрованной строке, и таким образом вы гарантируете, что у вас будет одна и та же соль как для шифрования сохраненного, так и для введенного пользователем пароля.