Генератор HMAC

Вычисляйте HMAC для текста, hex-байтов или файла с секретным ключом на основе SHA-256, SHA-384, SHA-512, SHA-1 или MD5 и сверяйте его с подписью вебхука или API. Данные не покидают ваш браузер.

Работает полностью в браузере. Данные никуда не загружаются.

Как пользоваться

  1. Выберите алгоритм. По умолчанию выбран HMAC-SHA256 — именно его используют большинство вебхуков и API.
  2. Введите ключ в поле «Секретный ключ» и выберите в параметре «Формат ключа» значение «Текст» (читается как UTF-8), «Hex» или «Base64». Hex или Base64 нужны, когда ключ представляет собой набор байтов, а не строку, похожую на пароль.
  3. Введите или вставьте сообщение, переключите формат ввода на «Hex» для необработанных байтов или нажмите «Открыть файл». HMAC обновляется по мере ввода.
  4. В параметре «Формат вывода» выберите «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 отличается от обычного хеша?
Обычный хеш, например SHA-256, может вычислить кто угодно, поэтому он показывает лишь, что данные не изменились случайно. HMAC можно вычислить только с секретным ключом, поэтому действительный HMAC также показывает, что его создал тот, у кого есть этот ключ.
HMAC — это то же самое, что цифровая подпись?

Нет. Обе стороны HMAC используют один общий секрет, поэтому любой, кто может проверить тег, может и создать его. Подписи вроде RSA или ECDSA (RS256 или ES256 в JWT) создаются закрытым ключом и проверяются открытым, поэтому те, кто их проверяет, не могут их подделать.

Отправляется ли мой секретный ключ на сервер?

Нет. HMAC-SHA1 и варианты SHA-2 вычисляются с помощью Web Crypto API вашего браузера, а HMAC-MD5 — кодом JavaScript на странице. Ключ и сообщение никогда не покидают ваше устройство. И всё же обращайтесь с секретами рабочих систем осторожно и заменяйте ключ, если он мог быть раскрыт.