Did you know that you can’t use == or === for password comparison in PHP?

Aug 26, 2020 PHP

PHPers, aren't you using comparison operators ("==" or "===") when comparing input data with stored information for important strings?

This is actually don’t use, so be careful.

Why not?

(Fixed on Aug. 31, 2020) It was fixed because the function name was typo. (Correct) memcmp/(wrong) strcmp.

Many languages, including PHP, use memcmp() internally when comparing strings. In the normal case (which doesn’t require strict security), using comparison operators is perfectly fine. This function is vulnerable when comparing strings that should never be guessed, such as passwords. Since memcmp() internally compares and verifies each byte, you can guess how many characters were correct from the beginning based on the response time. Such an attack is called a “timing attack”.

What should I do?

PHP provides a function called hash_equals() that compares strings using hash values. The description of the function also says “Timing attack safe string comparison”.

https://www.php.net/manual/en/function.hash-equals.php

Correct implementation when dealing with passwords

(Added on August 28, 2020) Added based on @anirfa’s comment.

Up to this point, there have been points to note when handling “important character strings” stored in plain text. For example, passwords and other information that should not be read by a third party should not be stored in plain text, but should be hashed and stored. PHP has the following functions for password management, so please use that.

I took a look at the implementation

Let’s look at the PHP source code to see if strcmp() is really used in the comparison operator.

zend_string.h#L316-L335

#if defined(__GNUC__) && (defined(__i386__) || (defined(__x86_64__) && !defined(__ILP32__)))
BEGIN_EXTERN_C()
ZEND_API zend_bool ZEND_FASTCALL zend_string_equal_val(zend_string *s1, zend_string *s2);
END_EXTERN_C()
#else
static zend_always_inline zend_bool zend_string_equal_val(zend_string *s1, zend_string *s2)
{
return !memcmp(ZSTR_VAL(s1), ZSTR_VAL(s2), ZSTR_LEN(s1));
}
#endif

static zend_always_inline zend_bool zend_string_equal_content(zend_string *s1, zend_string *s2)
{
return ZSTR_LEN(s1) == ZSTR_LEN(s2) && zend_string_equal_val(s1, s2);
}

static zend_always_inline zend_bool zend_string_equals(zend_string *s1, zend_string *s2)
{
return s1 == s2 || zend_string_equal_content(s1, s2);
}

zend_string_equals->zend_string_equal_content->zend_string_equal_val->memcmp is called. *Explanation of the processing when the inline assembler can be used (zend_string.c#L396-431) Omit