strcmp vs. == vs. === в PHP для проверки равенства хэшей

Я использую crypt() для хеширования паролей в PHP и пытаюсь разработать самый безопасный способ проверки равенства полученного хэша при проверке паролей.

Я вижу три варианта:

Вариант 1 — двойное равно

function checkPassword($hash, $password)
{
    return crypt($password, $hash) == $hash;
}

Вариант 2 — Тройное равно

function checkPassword($hash, $password)
{
    return crypt($password, $hash) === $hash;
}

Вариант 3 – strcmp()

function checkPassword($hash, $password)
{
    return strcmp(crypt($password, $hash), $hash) === 0;
}

Моя интуиция подсказывает мне, что вариант 1 — плохая идея из-за отсутствия проверки типов, а варианты 2 или 3, скорее всего, будут лучше. Однако я не могу понять, есть ли конкретный случай, в котором === или strcmp не сработают. Что безопаснее для этой цели?


person Polynomial    schedule 05.02.2013    source источник
comment
Прочтите: raz0r.name/vulnerabilities/simple-machines-forum   -  person user956584    schedule 05.02.2013
comment
@Userpassword Интересная ошибка! Интересно, как PHP обрабатывает такие строки, как $2a$10$...$, когда они преобразуются в числа...   -  person Polynomial    schedule 05.02.2013
comment
Если вы просто хотите надежно сравнить хэши, просто используйте ===. Если вы действительно заботитесь о безопасности и потенциальных атаках по времени (даже несмотря на дрожание сети), вам следует взглянуть на в этом или в этом обсуждении или используйте hash_equals() функция (PHP 5.6+).   -  person caw    schedule 17.07.2014


Ответы (4)


Когда дело доходит до безопасности, я предпочитаю использовать оператор ===. === гарантирует, что два операнда абсолютно одинаковы, не пытаясь приспособить какое-либо приведение, чтобы «помочь» сравнению достичь успешного совпадения, поскольку это может помочь при разработке благодаря языку со свободной типизацией, такому как PHP.

Конечно, одному из операндов следует доверять. Хэш из базы данных заслуживает доверия, а пользовательский ввод — нет.

Всегда можно немного поколебаться, придя к выводу, что нет риска использовать == в конкретном случае. Может быть. Но например

  "0afd9f7b678fdefca" == 0 is true
  "aafd9f7b678fdefca" == 0 is also true

поскольку PHP пытается преобразовать «хэш» в число (вероятно, используя atoi), что дает 0. Хотя маловероятно, что crypt возвращает 0, я бы предпочел максимизировать случаи, когда пароли не сопоставить (и ответить на звонок в службу поддержки), используя ===, чем разрешить редкий случай, о котором я не подумал, используя ==.

Что касается strcmp, функция возвращает <0 или >0, если они разные, и 0, если равны. Но

  strcmp("3", 0003) returns 0
  strcmp("0003", 0003) returns -3

что не удивительно в конце концов. Литерал 0003 на самом деле является целым числом, 3, и поскольку strcmp ожидает строку, 3 будет преобразовано в "3". Но это показывает, что в этом случае может произойти некоторое преобразование, поскольку strcmp — это функция, а === — часть языка.

Так что я предпочитаю в этом случае === (что в любом случае быстрее, чем ==).

person Breaking not so bad    schedule 05.02.2013
comment
Это самый разумный ответ, который я видел. Я согласен с тем, что и тип, и значение важны, чтобы избежать проблем с хелпером приведения. - person Polynomial; 05.02.2013
comment
Обратите внимание, что hash_equals() следует использовать при тестировании хеш-значений, созданных сильной функцией, такой как password_hash. - person Polynomial; 27.11.2014

Вы должны использовать функцию hash_equals(), встроенную в PHP. Не было бы необходимости делать свою собственную функцию. hash_equals() вернет логическое значение.

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

person bradc14    schedule 21.11.2014
comment
Теперь это правильный ответ для значений, сгенерированных password_hash, но hash_equals не существовало в основном PHP на момент запроса, и он не применяется к плоским хэшам (которые в любом случае не должны использоваться, но иногда требуются для устаревших ) - person Polynomial; 27.11.2014

Это неправильно, пожалуйста, посмотрите на определение функции. Согласно PHP:

Returns < 0 if str1 is less than str2;

> 0 if str1 is greater than str2,

and 0 if they are equal

Он возвращает меньше 0, если str1 меньше, чем str2. Обратите внимание на фразу «меньше чем», она возвращает не просто -1, а любое отрицательное значение. То же самое происходит, когда str1 больше, чем str2, но возвращает положительное, ненулевое значение. Он возвращает положительное значение, которое может быть равно 1 или любому последующему числу.

strcmp()возвращает число, представляющее собой разницу между двумя строками, начинающимися с последнего символа, который оказался похожим.

Вот пример:

$output = strcmp("red", "blue");

Переменная $output со значением 16

person Nadeem    schedule 13.09.2013

Я думаю, что в вашем случае будет достаточно использования ==.

== проверяет равенство независимо от типа, тогда как === проверяет равенство так же, как и тип.

1 == "1" = Верно

1 === "1" = Ложь

Так как мы не слишком заботимся о шрифте, я бы не стал усложнять и выбрал ==.

person Ian P    schedule 05.02.2013
comment
Я знаю о различиях между == и ===, но я пытаюсь выяснить, есть ли какой-то конкретный случай, связанный с безопасностью, который я упустил. Например, есть ли причина не использовать strcmp? - person Polynomial; 05.02.2013
comment
Поскольку вас не интересует безопасное двоичное сравнение, я бы не стал использовать strcmp. Вас не волнует, насколько различны строки — вас просто интересует, одинаковы ли они семантически. - person Ian P; 05.02.2013
comment
Что делать, если bcrypt по какой-то причине не работает? Возможно ли, чтобы нестроковое возвращаемое значение считалось равным строковому значению для $hash, где эта переменная является допустимым хешем? - person Polynomial; 05.02.2013
comment
Короче говоря, нет. Это не должно меняться. - person Ian P; 05.02.2013
comment
$хэш = function_returing_0(); var_dump(('00somehash' == $test)); возвращает true, поэтому использование только double == без приведения строки является дырой в безопасности. - person gries; 05.02.2013
comment
Что ж, тогда возникает вопрос: может ли $hash быть нулем? или .. Откуда берется $hash? - person Ian P; 05.02.2013
comment
@gries В этом случае хэши всегда будут начинаться с $2a$10, поэтому я думаю, что мы можем не учитывать эту проблему, но стоит быть в безопасности. - person Polynomial; 05.02.2013
comment
ага пока алгоритм хеширования не меняется, и я уверен в этом случае никто не заметит проблемы со сравнением == без приведения - person gries; 05.02.2013
comment
if( asdf == 0 ) die(d'oh\n); - person Antti Rytsölä; 05.02.2013
comment
@IanP нет, это не вопрос, ему нужна функция, которая надежно сравнивает 2 строки, мы НЕ должны заботиться о том, как должны или могут выглядеть строки, потому что эти вещи могут измениться - person gries; 05.02.2013