=== порівнює рядки посимвольно і зупиняється на першій розбіжності. Отже, час порівняння залежить від того, скільки перших символів збіглося. Вимірюючи час відповіді на багатьох запитах, нападник може підбирати секрет символ за символом - це timing-атака.
hash_equals() порівнює рядки за сталий час: завжди проходить усю довжину, незалежно від того, де розбіжність.
// Перевірка підпису вебхука
$expected = hash_hmac('sha256', $payload, $secret);
if (! hash_equals($expected, $request->header('X-Signature'))) {
abort(403);
}
Де це потрібно: скрізь, де рядок від користувача порівнюється із секретом - підписи вебхуків, API-токени, коди підтвердження, CSRF-токени.
Що варто знати:
- Першим аргументом іде відомий рядок, другим - отриманий від користувача.
- Довжину
hash_equalsне приховує: рядки різної довжини даютьfalseодразу. Тому порівнюють хеші чи HMAC фіксованої довжини, а не сирі секрети. - Для паролів не потрібен ні
===, ніhash_equals:password_verify()уже порівнює безпечно.
Laravel використовує hash_equals усередині - наприклад, у перевірці CSRF-токена й підписаних URL.