Сравнение паролей с 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?
}    

Спасибо за ваше время

Ответы

Ответ 1

Следующий пример кода может ответить на ваши вопросы.

Чтобы генерировать хешированный пароль с помощью Blowfish, вам сначала нужно сгенерировать соль, которая начинается с $2a $, за которой следует число итераций и 22 символа строки Base64.

$salt = '$2a$07$usesomadasdsadsadsadasdasdasdsadesillystringfors';
$digest = crypt('rasmuslerdorf', $salt);

Сохраняйте весь $digest в базе данных, он имеет как соль, так и дайджест.

При сравнении пароля просто сделайте это,

  if (crypt($user_input, $digest) == $digest)

Вы повторно используете дайджест как соль. crypt знает, сколько времени соль из идентификатора алгоритма.

Ответ 2

Цитата из руководства

CRYPT_BLOWFISH - хеширование Blowfish с соль следующим образом: "$ 2a $", двузначная параметр стоимости, "$" и 22 base 64 цифры из алфавита

Примечание: 22 базовые 64 цифры

Ответ 3

BCrypt использует 128 бит для соли, поэтому 22 байта Base64, используя только два бита последнего байта.

Хэш вычисляется с использованием соли и пароля. Когда вы передаете зашифрованный пароль, алгоритм считывает силу, соль (игнорируя все за ее пределами) и пароль, который вы дали, и вычисляет хэш, добавляя его. Если у вас есть PostgreSQL и pg_crypto, SELECT gen_salt ('bf'); покажет вам, что из $salt читается.

Вот пример кода для генерации соли, из моей . NET-реализации test-vector-gen.php, альтернативно:

$salt = sprintf('$2a$%02d$%s', [strength goes here],
    strtr(str_replace(
    '=', '', base64_encode(openssl_random_pseudo_bytes(16))
    ),
    'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/',
    './ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'));

Нет причин использовать ту же соль для всех ваших паролей. В любом случае соль является частью выхода, поэтому вы ничего не получаете в удобстве... хотя я предоставляю PHP, что у вас должна быть встроенная функция gen_salt.

Ответ 4

Новая соль для каждого пароля

$password = '[email protected]';

$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

Ответ 5

Первый вопрос:

Поэтому это, по определению, не должно работать:

echo crypt('rasmuslerdorf', '$2a$07$usesomadasdsadsadsadasdasdasdsadesillystringforsalt$');

Где кажется, что crypt() разрезает сама соль до длины 22. Может ли кто-нибудь объяснить это?

Нет проблем с наличием слишком большого количества символов... фраза Использование символов вне этого диапазона в соли вызовет crypt(), чтобы вернуть строку нулевой длины, выходящую за пределы диапазона базы 64, а не диапазон 22 символов. Попробуйте поместить недопустимый символ в строку солей, и вы должны обнаружить, что вы получаете пустой вывод (или если вы помещаете < 22 символа в, что приводит к незаконным пустым байтам).

Второй вопрос:

Вы передаете зашифрованный хранимый пароль как соль, потому что в зашифрованной строке всегда отображается (по дизайну) соляная строка, и таким образом вы гарантируете, что у вас есть такая же соль для шифрования сохраненного и введенного пользователем пароля.

Ответ 6

Этот вопрос касается моего ответа на ответ ZZ Coder. В основном мой вопрос заключается в сохранении результата crypt() в базе данных. Я должен хранить весь вывод в базе данных, чтобы моя база данных выглядела так:

--------------------------------------------------------------------------------
| ID | Username |                          Password                            |
--------------------------------------------------------------------------------
| 32 | testuser | $2a$07$usesomadasdsadsadsadaeMTUHlZEItvtV00u0.kb7qhDlC0Kou9e |
--------------------------------------------------------------------------------

Если да, то разве это не значит, что цель использования соли в первую очередь? Если кто-то получает доступ к db, они могут ясно видеть соль, используемую для шифрования?

Бонусный вопрос: безопасно ли использовать одну соль для каждого пароля?