Current Location: Home> Latest Articles> Common Mistakes in Using the hash_equals Function for Password Comparison and How to Fix Them

Common Mistakes in Using the hash_equals Function for Password Comparison and How to Fix Them

gitbox 2025-06-27

In PHP, the hash_equals function is used to securely compare two hashed strings. It was specifically designed to mitigate timing attacks, which occur when an attacker infers whether a password is correct by measuring small differences in processing time. However, developers often misuse this function in ways that compromise its intended security benefits. This article explores those common mistakes and how to correct them.

1. Misunderstanding: Confusing hash_equals with Regular String Comparison

The purpose of hash_equals is to prevent information leakage through timing differences in string comparisons. Unlike the traditional == or === operators, hash_equals performs a constant-time comparison that does not depend on the string length or content, thereby defending against timing attacks.

A common mistake is using regular string comparison instead of hash_equals. For example:

<span><span><span class="hljs-keyword">if</span></span><span> (</span><span><span class="hljs-variable">$password</span></span><span> == </span><span><span class="hljs-variable">$storedPasswordHash</span></span><span>) {
    </span><span><span class="hljs-keyword">echo</span></span><span> </span><span><span class="hljs-string">"Password is correct!"</span></span><span>;
}
</span></span>

Even though hashes are being compared, using == or === may cause PHP to optimize the comparison based on the content or length of the hashes, leaking timing information.

Solution:
Always use hash_equals for comparing hashed values:

<span><span><span class="hljs-keyword">if</span></span><span> (</span><span><span class="hljs-title function_ invoke__">hash_equals</span></span><span>(</span><span><span class="hljs-variable">$password</span></span><span>, </span><span><span class="hljs-variable">$storedPasswordHash</span></span><span>)) {
    </span><span><span class="hljs-keyword">echo</span></span><span> </span><span><span class="hljs-string">"Password is correct!"</span></span><span>;
}
</span></span>

2. Misunderstanding: Using hash_equals to Compare Plaintext Passwords with Hashes

Another frequent mistake is using hash_equals to compare a plaintext password directly with a hashed string. Developers might mistakenly believe they can compare the raw password with the hash this way:

<span><span><span class="hljs-comment">// Incorrect usage</span></span><span>
</span><span><span class="hljs-keyword">if</span></span><span> (</span><span><span class="hljs-title function_ invoke__">hash_equals</span></span><span>(</span><span><span class="hljs-variable">$password</span></span><span>, </span><span><span class="hljs-title function_ invoke__">password_hash</span></span><span>(</span><span><span class="hljs-variable">$password</span></span><span>, PASSWORD_BCRYPT))) {
    </span><span><span class="hljs-keyword">echo</span></span><span> </span><span><span class="hljs-string">"Password is correct!"</span></span><span>;
}
</span></span>

This is incorrect because password_hash generates a new hash each time, and hash_equals is only suitable for comparing two static hashes.

Solution:
Use password_verify to check whether a plaintext password matches a stored hash. This function is designed specifically for this purpose:

<span><span><span class="hljs-keyword">if</span></span><span> (</span><span><span class="hljs-title function_ invoke__">password_verify</span></span><span>(</span><span><span class="hljs-variable">$password</span></span><span>, </span><span><span class="hljs-variable">$storedPasswordHash</span></span><span>)) {
    </span><span><span class="hljs-keyword">echo</span></span><span> </span><span><span class="hljs-string">"Password is correct!"</span></span><span>;
}
</span></span>

3. Misuse: Not Ensuring Consistent Hash Lengths

hash_equals compares not only the content of two hashes but also their lengths. If the lengths differ, the function immediately returns false without performing the comparison. Developers sometimes overlook differences in hash length, leading to failed comparisons.

For instance, modifying or truncating the hash before storing it may result in an inconsistent length.

Solution:
Make sure the hash is stored and compared in a consistent format. Avoid manually altering the output of password_hash, which returns a standardized hash string.

4. Misuse: Comparing Hashes from Different Algorithms

hash_equals is designed to compare hashes generated by the same algorithm. If you attempt to compare hashes from different algorithms (e.g., md5 vs. sha256), the comparison will fail, as the formats and lengths differ.

<span><span><span class="hljs-comment">// Incorrect usage: comparing md5 with sha256</span></span><span>
</span><span><span class="hljs-keyword">if</span></span><span> (</span><span><span class="hljs-title function_ invoke__">hash_equals</span></span><span>(</span><span><span class="hljs-title function_ invoke__">md5</span></span><span>(</span><span><span class="hljs-variable">$password</span></span><span>), </span><span><span class="hljs-title function_ invoke__">sha256</span></span><span>(</span><span><span class="hljs-variable">$password</span></span><span>))) {
    </span><span><span class="hljs-keyword">echo</span></span><span> </span><span><span class="hljs-string">"Password is correct!"</span></span><span>;
}
</span></span>

Solution:
Always use the same hashing algorithm for both generating and verifying hashes. When using password_hash, the function automatically chooses a strong algorithm like BCRYPT or Argon2. Use password_verify for validation.

5. Misuse: Ignoring the Return Value of hash_equals

hash_equals returns a boolean value indicating whether the two hashes are equal. Failing to properly handle this return value can result in incorrect application behavior.

<span><span><span class="hljs-comment">// Incorrect usage: ignoring the return value</span></span><span>
</span><span><span class="hljs-keyword">if</span></span><span> (</span><span><span class="hljs-title function_ invoke__">hash_equals</span></span><span>(</span><span><span class="hljs-variable">$password</span></span><span>, </span><span><span class="hljs-variable">$storedPasswordHash</span></span><span>)) {
    </span><span><span class="hljs-keyword">echo</span></span><span> </span><span><span class="hljs-string">"Password is correct!"</span></span><span>;
} </span><span><span class="hljs-keyword">else</span></span><span> {
    </span><span><span class="hljs-keyword">echo</span></span><span> </span><span><span class="hljs-string">"Password is incorrect!"</span></span><span>;
}
</span></span>

The error here isn't with hash_equals itself, but in not following up with appropriate action based on its result. To maintain secure password handling, it's crucial to handle both outcomes correctly.

Solution:
Make sure your logic fully respects the return value of hash_equals:

<span><span><span class="hljs-keyword">if</span></span><span> (</span><span><span class="hljs-title function_ invoke__">hash_equals</span></span><span>(</span><span><span class="hljs-variable">$password</span></span><span>, </span><span><span class="hljs-variable">$storedPasswordHash</span></span><span>)) {
    </span><span><span class="hljs-keyword">echo</span></span><span> </span><span><span class="hljs-string">"Password is correct!"</span></span><span>;
} </span><span><span class="hljs-keyword">else</span></span><span> {
    </span><span><span class="hljs-comment">// Add secure error handling, such as logging</span></span><span>
    </span><span><span class="hljs-keyword">echo</span></span><span> </span><span><span class="hljs-string">"Invalid password!"</span></span><span>;
}
</span></span>

6. Conclusion

hash_equals is a critical PHP function for defending against timing attacks, but it is not a one-size-fits-all solution. Improper usage can introduce security vulnerabilities. Common mistakes include confusing it with regular comparisons, misusing it to compare plaintext to hashes, overlooking hash length consistency, mixing hash algorithms, and ignoring its return value. The key is to understand when and how to use hash_equals and to rely on password_verify for password validation.

By following best practices and using the appropriate tools, you can significantly enhance the security of your password verification process and minimize exposure to attack vectors.