strcmp vs. == vs. === in PHP for checking hash equality - php

I'm using crypt() to hash passwords in PHP, and am trying to work out the safest way of testing equality of the resulting hash when performing password checks.
There are three options that I can see:
Option 1 - Double Equals
function checkPassword($hash, $password)
{
return crypt($password, $hash) == $hash;
}
Option 2 - Triple Equals
function checkPassword($hash, $password)
{
return crypt($password, $hash) === $hash;
}
Option 3 - strcmp()
function checkPassword($hash, $password)
{
return strcmp(crypt($password, $hash), $hash) === 0;
}
My intuition tells me that option 1 is a bad idea, due to the lack of type checking, and that options 2 or 3 are likely to be better. However, I can't work out if there's a specific case that === or strcmp would fail under. Which is safest for this purpose?

When it comes to security I prefer to use the === operator. === ensures the two operands are exactly the same, without trying to accomodate some casting in order to "help" the comparison to reach a successful match - as it may help while developing thanks to a loose-typed language, like PHP.
Of course, one of the operand is to be trusted. A hash from the database is trustable, while the user input is not.
One can always dither for a while, coming to the conclusion there is no risk using == in a specific case. Maybe. But for instance
"0afd9f7b678fdefca" == 0 is true
"aafd9f7b678fdefca" == 0 is also true
as PHP tries to convert the "hash" into a number (probably using atoi) which gives 0. While it is unlikely crypt returns 0, I'd prefer to maximize the cases where the passwords don't match (and answer a support call) by using ===, than allowing a rare case that I didn't think about by using ==.
As for strcmp, the function returns <0 or >0 if different, and 0 if equal. But
strcmp("3", 0003) returns 0
strcmp("0003", 0003) returns -3
which are not surprising after all. A literal 0003 is actually an integer, 3 and since strcmp expects a string, the 3 will be converted to "3". But that shows there is some conversion that may happen in this case, since strcmp is a function, while === is part of the language.
So my preference in that case goes to === (which is faster than == anyway).

You should be using the hash_equals() function that is built into PHP. There would be no need to make your own function. The hash_equals() will return a boolean value.
In my opinion it is usually NOT a good idea to use == or === for comparing strings let alone hashed strings.

That is incorrect, please look at the definition of the function.
According to PHP:
Returns < 0 if str1 is less than str2;
> 0 if str1 is greater than str2,
and 0 if they are equal
It returns less than 0 if str1 is less than str2. Note the phrase "less than", it does not return just -1, but any negative value. The same happens when str1 is greater than str2, but it returns a positive, non-zero value. It returns a positive value that can be 1, or any number thereafter.
strcmp()returns a number that is the difference between the two strings starting with the last character that was found to be similar.
Here is an example:
$output = strcmp("red", "blue");
The variable $output with contain a value of 16

I think that using == would be sufficient in your case.
== checks for equality regardless of type, whereas === checks for equality as well as type.
1 == "1" = True
1 === "1" = False
Since we're not too concerned with type, I'd keep it simple and go with ==.

Related

Python: What is the equivalent of PHP "=="

The question says it.
Here's a little explanation.
In PHP. "==" works like this
2=="2" (Notice different type)
// True
While in python:
2=="2"
// False
2==2
// True
The equivalent for python "==" in php is "==="
2===2
//True
2==="2"
//False
Million dollar question. What is php "==" equivalent to in python?
Python doesn't coerce between types the way PHP does, mostly.
You'll have to do it explicitly:
2 == int('2')
or
str(2) == '2'
Python coerces numeric types (you can compare a float with an integer), and Python 2 also auto-converts between Unicode and byte string types (to the chagrin of many).
There isn't one. You need to convert types before checking for equality. In your example, you could do
2==int("2")
There is no equivalent.
The difference is simply that Python is strong-typed where PHP is not, so a comparison between two types in Python would always return false. Unless you explicitly cast to the type of the other part of the comparison of course.
You could also have
str(2) == "2"
so here is the equivalent to php's ==
def php_cmp(a, b):
if a is None and isinstance(b, basestring):
a = ""
elif b is None and isinstance(a, basestring):
b = ""
if a in (None, False, True) or b in (None, False, True):
return bool(a) - bool(b)
if isinstance(a, (basestring, int, long, float)) and isinstance(b, (basestring, int, long, float)):
try:
return cmp(float(a), float(b))
except ValueError:
return cmp(a,b)
if isinstance(a, (tuple,list)) and isinstance(b, (tuple,list)):
if len(a) != len(b):
return cmp(len(a),len(b))
return cmp(a,b)
if isinstance(a, dict) and isinstance(b, dict):
if len(a) != len(b):
return cmp(len(a),len(b))
for key in a:
if key not in b:
raise AssertionError('not compareable')
r = cmp(a[key], b[key])
if r: return r
return 0
if isinstance(a, (basestring, int, long, float)):
return 1
if isinstance(b, (basestring, int, long, float)):
return -1
return cmp(a,b)
def php_equal(a, b):
return php_cmp(a,b) == 0
Test:
>>> php_equal(2, '2')
True
Due to a different object model and array implementation this is not 100% correct, but should give you some idea which problems might occur with automatically converting types for comparison.
May be this's something similar:
x = 5
y = "5"
str(x) == str(y)
True
str(2) == str("2")
True
The best answer one can give you is to avoid PHP-like comparison. Anyway, it leads you to hard-to-find errors and hours spent on debugging.
But it's okay if you want to write your own PHP interpreter in Python.
You probably want to cast variables first and compare them after. If you program fails on casting, you run into errors earlier preventing further damage.
Additional note
As an add-on to Martijn's answer ... since Python does not do implicit type coercion like PHP, you can use:
type(2)
or
type("2")
or
type(<expression>)
if you need to know the relevant type that Python uses internally, in order to do your expressly specified type coercion.

Why are strings "0e368798" and "00000000" equal, per PHP's equality operator?

Can someone explain why the following two statements return true?
if ('0e368798' == '00000000')
or
if ((string)'0e368798' == (string)'00000000')
Why do I have to use the strict operator to check the equality of these two strings?
Because XeY is X * 10^(Y), and 0 times anything is 0. 0000000 is also 0. And == in PHP very intuitively thinks that if it can be converted into a number, it should be.
EDIT: It was in a helpful comment that is now deleted, so with apologies to the commenter whose name I did not catch, I will repeat it here - from PHP docs on comparison:
If you compare a number with a string or the comparison involves numerical strings, then each string is converted to a number and the comparison performed numerically. These rules also apply to the switch statement. The type conversion does not take place when the comparison is === or !== as this involves comparing the type as well as the value.

Explanation about constant-time algorithm and string comparision

I've a problem to understand two different ways of string comparison. Given is the following function which compares two strings.
This function is used in the Symfony-Framework security component to compare passwords in the user-login process.
/**
* Compares two strings.
*
* This method implements a constant-time algorithm to compare strings.
*
* #param string $knownString The string of known length to compare against
* #param string $userInput The string that the user can control
*
* #return Boolean true if the two strings are the same, false otherwise
*/
function equals($knownString, $userInput)
{
// Prevent issues if string length is 0
$knownString .= chr(0);
$userInput .= chr(0);
$knownLen = strlen($knownString);
$userLen = strlen($userInput);
$result = $knownLen - $userLen;
// Note that we ALWAYS iterate over the user-supplied length
// This is to prevent leaking length information
for ($i = 0; $i < $userLen; $i++) {
// Using % here is a trick to prevent notices
// It's safe, since if the lengths are different
// $result is already non-0
$result |= (ord($knownString[$i % $knownLen]) ^ ord($userInput[$i]));
}
// They are only identical strings if $result is exactly 0...
return 0 === $result;
}
origin: origin snippet
I've problem to understand the difference between the equals() function and a simple comparison ===. I wrote a simple working example to explain my problem.
Given strings:
$password1 = 'Uif4yQZUqmCWRbWFQtdizZ9/qwPDyVHSLiR19gc6oO7QjAK6PlT/rrylpJDkZaEUOSI5c85xNEVA6JnuBrhWJw==';
$password2 = 'Uif4yQZUqmCWRbWFQtdizZ9/qwPDyVHSLiR19gc6oO7QjAK6PlT/rrylpJDkZaEUOSI5c85xNEVA6JnuBrhWJw==';
$password3 = 'iV3pT5/JpPhIXKmzTe3EOxSfZSukpYK0UC55aKUQgVaCgPXYN2SQ5FMUK/hxuj6qZoyhihz2p+M2M65Oblg1jg==';
Example 1 (act as expected)
echo $password1 === $password2 ? 'True' : 'False'; // Output: True
echo equals($password1, $password2) ? 'True' : 'False'; // Output: True
Example 2 (act as expected)
echo $password1 === $password3 ? 'True' : 'False'; // Output: False
echo equals($password1, $password3) ? 'True' : 'False'; // Output: False
I read about the Karp Rabin Algorithm but I'm not sure if the equals() function represent
the Karp Rabin Algorithm, and in general I didn't understand the Wikipedia article.
On the other hand I read that the equals() function will prevent brute force attacks is that right? Can someone explain what the advantage for equals() is?
Or can someone give me an example where === will fail and equals() does the correct work, so I can understand the advantage?
And what does constant-time Algorithm mean? I think constant-time has nothing to do with the real time, or if I'm wrong?
This function is just a normal string comparison function. It is not Rabin Karp. It is NOT constant time, it's linear time, regardless of what the comment says. It also does not prevent brute force attacks.
How it works:
if the correct and user-provided passwords are of different length, make $result != 0
iterate over the user-provided password, xor each of its characters with the corresponding character of the correct password (if the correct password is shorter, keep going through it in a circle), and bitwise or each result with $result.
Since only bitwise or is used, if any of the characters are different, $result will be != 0. Step 1 is needed because otherwise, user input "abca" would be accepted if the real password was "abc".
Why such string comparison functions are sometimes used
Let's assume we compare strings the usual way, and the correct password is "bac". Let's also assume I can precisely measure how long it takes for the password check to complete.
I (the user) try a, b, c... They don't work.
Then, I try aa. The algorithm compares the first 2 letters - b vs a, sees it's wrong, and returns false.
I now try with bb. The algorithm compares b vs b, they match, so it goes on to letter #2, compares a vs b, sees it's wrong, returns false. Now, since I am able to time the execution of the algorithm precisely, I know the password starts with "b", because the second pass took more time than the first one - I know the first letter matched.
So I try ba, bb, bc... They fail.
Now I check baa, bbb, see baa runs slower so second letter is a. This way, letter by letter, I can determine the password in an O(cN) number of attempts instead of O(c^N) that brute force would take.
It usually isn't as much of a concern as this explanation might make it sound, because it is unlikely an attacker will time a string comparison to such a degree of accuracy. But sometimes it can be.

Weird behaviour with if statement

The following appears to work, yet I really don't think it should:
if ("'True','False'" == 0)
{
echo 'Hello, World.';
}
Is it grepping the last False out of the string, and if so why, and how do you stop it?
This is due to loose typing. When you compare a string to a number, PHP has to cast one to the other's type. In this case, the string, when casted to an integer, is worth 0, your condition being True.
Any string evaluates to 0 when being compared against a number (unless it's a stringed number such as "5" or "4string"). That's why it'll always evaluate to 0.
See the Type Comparison Table
What's really happening is that PHP is trying to do an implicit type conversion, and for one reason or another it's deciding that string when converted to an integer looks like 0. You can prove this to yourself by doing:
echo ((int) "'True','False'");
If you want to do a type-checked comparison you should use the triple equals (===):
if("'True','False'" === 0)
...which will most certainly evaluate to false.
No; it's converting the string "'True','False'" to a number — and that number is 0. 0 == 0 is true. You can solve it by using !"'True','False'" if I understand your intent correctly, or by using strict equality, ===, which you should generally use.

PHP: Why do we need string comparison function?

The comparision operators < <= > >= can be applied for strings as well. So why do we need special function for string comparision: strcmp ?
Because there are several variations:
Depending on the function, the answer to these questions vary:
Is it case sensitive? (strcmp vs strcasecmp, strnatcmp vs strnatcasecmp)
Depends it depend on the locale? (strcoll does)
Can I specify a collation? (strcoll is affected by setlocale)
Additionaly, the comparison operators also give true or false. strcmp gives an integer so it can encode simultaneously whether there's identity (return 0) or, if it not, which is is bigger (depending on whether the value is positive or negative).
Although there are no overloads in PHP for strcmp, strcmp results in 3 different values
-1 for less than, 0 for equals and +1 for greater than the compared string. With < = <= > >= you will have (sometimes) to do multiple checks one after another.

Categories