Как пользоваться
- Выберите алгоритм. По умолчанию выбран HMAC-SHA256 — именно его используют большинство вебхуков и API.
- Введите ключ в поле «Секретный ключ» и выберите в параметре «Формат ключа» значение «Текст» (читается как UTF-8), «Hex» или «Base64». Hex или Base64 нужны, когда ключ представляет собой набор байтов, а не строку, похожую на пароль.
- Введите или вставьте сообщение, переключите формат ввода на «Hex» для необработанных байтов или нажмите «Открыть файл». HMAC обновляется по мере ввода.
- В параметре «Формат вывода» выберите «hex (нижний регистр)», «HEX (верхний регистр)» или «Base64» и скопируйте результат либо вставьте полученную подпись в поле «Сравнить с», чтобы увидеть «Совпадает» или «Не совпадает».
Как работает HMAC
HMAC (hash-based message authentication code, код аутентификации сообщений на основе хеширования, RFC 2104) объединяет криптографическую хеш-функцию с секретным ключом. Правильное значение для сообщения может получить только тот, кто знает ключ, поэтому совпадение HMAC доказывает сразу две вещи: сообщение не изменялось и оно пришло от владельца ключа.
Конструкция выглядит так: H((K ⊕ opad) ‖ H((K ⊕ ipad) ‖ message)). Если ключ длиннее размера блока хеш-функции (64 байта для MD5, SHA-1 и SHA-256, 128 для SHA-384 и SHA-512), он сначала хешируется. Затем ключ дополняется нулями до этого размера и объединяется операцией XOR с ipad (байты 0x36) для внутреннего хеша по сообщению и с opad (байты 0x5c) для внешнего хеша по внутреннему результату. У наивной конструкции SHA-256(key ‖ message) нет внешнего хеша, поэтому любой, кто видел один тег, может дописать данные к сообщению и вычислить новый действительный тег без ключа (атака удлинением сообщения).
Длина результата равна длине выхода базовой хеш-функции: для HMAC-SHA256 это 32 байта, или 64 шестнадцатеричных символа. RFC 2104 рекомендует ключ как минимум такой же длины; подходящие ключи создаёт генератор случайных строк Base64.
Где используются подписи HMAC
- Вебхуки GitHub. Заголовок
X-Hub-Signature-256содержитsha256=, за которым следует HMAC-SHA256 необработанного тела запроса в hex, вычисленный с секретом вебхука в качестве ключа. Значение заголовка можно целиком вставить в поле «Сравнить с»: префиксsha256=игнорируется. - Вебхуки Stripe. Заголовок
Stripe-Signatureсодержит метку времениtи подписьv1— HMAC-SHA256 в hex от метки времени, точки и необработанного тела, вычисленный с секретом конечной точки (whsec_...), который используется как текст. - Вебхуки Shopify.
X-Shopify-Hmac-Sha256содержит HMAC-SHA256 необработанного тела в Base64, вычисленный с секретом клиента (client secret) приложения, поэтому выберите формат вывода «Base64» или вставьте значение заголовка в поле «Сравнить с». - AWS Signature Version 4. Ключ подписи получается цепочкой вычислений HMAC-SHA256: сначала дата с ключом
AWS4плюс секретный ключ доступа, затем регион, сервис иaws4_request, каждый раз с предыдущим результатом в качестве ключа. Выберите в параметре «Формат ключа» значение «Hex», чтобы передавать результат одного шага на вход следующему. - JSON Web Token. HS256, HS384 и HS512 — это HMAC-SHA256, -384 и -512 от закодированного заголовка, точки и закодированной полезной нагрузки. Третья часть токена — этот HMAC в Base64, безопасном для URL, и поле «Сравнить с» принимает её как есть. Декодер JWT проверяет токены целиком.
Всегда вычисляйте HMAC по необработанному телу запроса точно в том виде, в каком оно было получено. Разбор JSON и повторная сериализация меняют пробелы или порядок ключей, а даже один лишний перевод строки даёт совершенно другой HMAC.
HMAC в коде
# Python
import hmac, hashlib
sig = hmac.new(b'key', b'message', hashlib.sha256).hexdigest()
hmac.compare_digest(sig, received) # сравнение за постоянное время
// Node.js
const crypto = require('crypto');
const sig = crypto.createHmac('sha256', 'key').update('message').digest('hex');
// timingSafeEqual выбрасывает исключение при разной длине, поэтому сначала сравните длины
crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(received));
// PHP: сообщение передаётся перед ключом
$sig = hash_hmac('sha256', 'message', 'key');
hash_equals($sig, $received);
# OpenSSL: текстовый ключ, затем тот же ключ в виде hex
printf '%s' 'message' | openssl dgst -sha256 -hmac 'key'
printf '%s' 'message' | openssl dgst -sha256 -mac HMAC -macopt hexkey:6b6579
# Тестовый вектор: ключ "key", сообщение "The quick brown fox jumps over the lazy dog"
# HMAC-SHA256 = f7bc83f430538424b13298e6aa6fb143ef4d59a14946175997479dbc2d1a3cd8
Проверяя подпись в собственном коде, используйте сравнение за постоянное время, как в примерах выше. Обычное == останавливается на первом несовпадающем символе, и по этой разнице во времени злоумышленник может подобрать действительную подпись байт за байтом. В схемах с меткой времени, таких как у Stripe, также отклоняйте запросы с меткой времени старше нескольких минут: это защищает от атак повторного воспроизведения.
Часто задаваемые вопросы
Безопасны ли ещё HMAC-MD5 и HMAC-SHA1?
Атаки на основе коллизий взламывают MD5 и SHA-1 как обычные хеш-функции, но не дают практического способа подделать HMAC, стойкость которого зависит от других свойств хеш-функции и от секретного ключа (HMAC-MD5 рассматривается в RFC 6151). Интеграции, которые их требуют, например коды TOTP на основе HMAC-SHA1, можно оставить как есть. В новых разработках используйте HMAC-SHA256.
Почему мой HMAC не совпадает с подписью вебхука?
Обычные причины: хешируется разобранный и заново сериализованный JSON вместо необработанного тела, в конце сообщения есть перевод строки, указан не тот секрет или формат ключа (секрет в Base64 или hex введён как «Текст» или наоборот) либо выбран не тот алгоритм. Hex или Base64 здесь значения не имеют: поле «Сравнить с» принимает hex в любом регистре, стандартный Base64 и Base64, безопасный для URL.
Чем HMAC отличается от обычного хеша?
HMAC — это то же самое, что цифровая подпись?
Нет. Обе стороны HMAC используют один общий секрет, поэтому любой, кто может проверить тег, может и создать его. Подписи вроде RSA или ECDSA (RS256 или ES256 в JWT) создаются закрытым ключом и проверяются открытым, поэтому те, кто их проверяет, не могут их подделать.
Отправляется ли мой секретный ключ на сервер?
Нет. HMAC-SHA1 и варианты SHA-2 вычисляются с помощью Web Crypto API вашего браузера, а HMAC-MD5 — кодом JavaScript на странице. Ключ и сообщение никогда не покидают ваше устройство. И всё же обращайтесь с секретами рабочих систем осторожно и заменяйте ключ, если он мог быть раскрыт.