HMAC (Hash-based Message Authentication Code) представляет собой механизм вычисления кода аутентификации сообщения на основе криптографической хеш-функции и секретного ключа. Его назначение — одновременно обеспечить целостность данных и их подлинность.
В отличие от обычных хеш-функций, HMAC не предназначен для одностороннего преобразования данных в фиксированный отпечаток. Он включает секретный ключ, без которого невозможно корректно вычислить или подделать итоговое значение.
Математически HMAC строится на следующей идее:
Это делает HMAC устойчивым к атакам, связанным с коллизиями хеш-функций, при условии, что сама хеш-функция криптографически стойкая (например, SHA-256).
HMAC используется в ситуациях, где необходимо доказать:
Типовые сценарии:
В отличие от цифровой подписи, HMAC является симметричным механизмом: один и тот же ключ используется для генерации и проверки кода.
Обычная хеш-функция:
HMAC:
Ключевой момент: даже зная алгоритм и входные данные, невозможно восстановить корректный HMAC без ключа.
В браузерной среде HMAC реализуется через интерфейс
SubtleCrypto, доступный как crypto.subtle.
Основные методы:
generateKeyimportKeysignverifyАлгоритм задаётся через объект конфигурации:
{
name: "HMAC",
hash: { name: "SHA-256" }
}
Поддерживаемые хеш-функции зависят от окружения, чаще всего используются:
Ключ создаётся через generateKey:
const key = await crypto.subtle.generateKey(
{
name: "HMAC",
hash: { name: "SHA-256" }
},
true,
["sign", "verify"]
);
Параметры:
true — ключ можно экспортироватьsign — разрешение на подписьverify — разрешение на проверкуКлюч в WebCrypto представлен как CryptoKey, а не как
строка или байтовый массив.
В реальных системах ключ чаще приходит извне и импортируется:
const rawKey = new Uint8Array([1, 2, 3, 4, 5, 6]);
const key = await crypto.subtle.importKey(
"raw",
rawKey,
{
name: "HMAC",
hash: { name: "SHA-256" }
},
false,
["sign", "verify"]
);
Формат "raw" означает использование «сырых» байтов
ключа.
Метод sign возвращает бинарный результат
(ArrayBuffer):
const encoder = new TextEncoder();
const data = encoder.encode("сообщение");
const signature = await crypto.subtle.sign(
"HMAC",
key,
data
);
Результат необходимо преобразовывать для удобного хранения или передачи, например в Base64:
function toBase64(buffer) {
const bytes = new Uint8Array(buffer);
let binary = "";
for (let i = 0; i < bytes.byteLength; i++) {
binary += String.fromCharCode(bytes[i]);
}
return btoa(binary);
}
const sigBase64 = toBase64(signature);
Для проверки используется verify:
const valid = await crypto.subtle.verify(
"HMAC",
key,
signature,
data
);
Результат:
true — подпись корректнаfalse — данные или ключ не совпадаютWebCrypto API работает исключительно с бинарными структурами:
ArrayBufferTypedArrayСтроки необходимо явно кодировать:
const encoder = new TextEncoder();
const encoded = encoder.encode("текст");
И декодировать при необходимости:
const decoder = new TextDecoder();
const decoded = decoder.decode(encoded);
Ключ HMAC должен оставаться строго конфиденциальным. Его компрометация полностью нарушает модель безопасности.
В отличие от некоторых схем шифрования, HMAC не требует уникальных nonce для каждого сообщения. Однако повторное использование одного и того же сообщения с тем же ключом всегда даст одинаковый результат.
HMAC защищён от:
Одна из самых частых проблем — сравнение строк вместо байтов. Даже различие в UTF-8 кодировке приводит к разным результатам.
HMAC нельзя корректно сравнивать как строку без преобразования:
ArrayBuffer ≠ строкаКлюч должен соответствовать требованиям алгоритма. Короткие или предсказуемые ключи снижают стойкость.
HMAC в Web Crypto API стандартизирован и поддерживается в современных браузерах:
В серверной среде аналогичный API отсутствует, но концептуально
совместим с Node.js crypto модулем при использовании
HMAC-SHA256.
const key = await crypto.subtle.generateKey(
{
name: "HMAC",
hash: { name: "SHA-256" }
},
true,
["sign", "verify"]
);
const encoder = new TextEncoder();
const message = encoder.encode("данные");
const signature = await crypto.subtle.sign(
"HMAC",
key,
message
);
const isValid = await crypto.subtle.verify(
"HMAC",
key,
signature,
message
);
Такой цикл отражает базовую модель применения HMAC в браузере: генерация ключа, подпись данных и проверка целостности.
HMAC часто выступает как слой защиты поверх обычного HTTP-трафика. Он применяется:
Его простота и высокая скорость вычисления делают его предпочтительным в системах, где требуется частая проверка целостности сообщений без затрат на асимметричную криптографию.