Звичайна хеш-функція на кшталт SHA-256 дає той самий результат для того самого повідомлення незалежно від того, хто його обчислює. Це добре для перевірки цілісності, але не захищає від підробки: будь-хто може змінити повідомлення й перерахувати новий правильний хеш. HMAC вирішує цю проблему, додаючи до хешування секретний ключ.
Як HMAC поєднує хеш і ключ
HMAC (Hash-based Message Authentication Code) обчислює хеш не лише від повідомлення, а й від секретного ключа, відомого лише відправнику й отримувачу. Обчислити правильний HMAC без знання ключа практично неможливо, тож збіг HMAC підтверджує і цілісність повідомлення, і те, що його створив власник ключа.
Чому це не просто "хеш повідомлення + ключ"
Наївне зчеплення ключа й повідомлення перед хешуванням (наприклад, hash(key + message)) вразливе до атак подовження повідомлення (length extension attack) для деяких хеш-функцій. HMAC використовує спеціальну конструкцію з подвійним хешуванням і внутрішнім/зовнішнім падингом, яка захищена від цього класу атак.
Де застосовується HMAC
HMAC лежить в основі підпису вебхуків (щоб отримувач міг переконатися, що запит прийшов саме від очікуваного сервісу), автентифікації API-запитів, а також є частиною TOTP-алгоритму для генерації одноразових кодів.
Навіщо це потрібно
- Перевіряти автентичність вебхуків від сторонніх сервісів (наприклад, платіжних систем).
- Підписувати API-запити без передачі самого секретного ключа по мережі.
- Розуміти внутрішню механіку TOTP-кодів і токенів сесій.
Порівняння в постійний час
Порівнювати отриманий HMAC з очікуваним звичайним === небезпечно: такий оператор зазвичай зупиняється на першому символі розбіжності, і час виконання порівняння видає зловмиснику, скільки перших символів він вгадав правильно (timing attack). Надійні реалізації використовують функції порівняння за постійний час (наприклад, hash_equals у PHP чи crypto.timingSafeEqual у Node.js), які завжди перевіряють усі байти незалежно від того, де перша розбіжність.