Защита от подделки данных с помощью HMAC

Принцип HMAC в контексте Web Crypto API

HMAC (Hash-based Message Authentication Code) представляет собой механизм проверки целостности и подлинности сообщения с использованием криптографической хеш-функции и общего секретного ключа. В Web Crypto API этот механизм реализуется через алгоритм HMAC, встроенный в интерфейс SubtleCrypto.

Основная идея заключается в том, что отправитель и получатель используют один и тот же секретный ключ. На основе этого ключа и сообщения вычисляется подпись. Любое изменение данных приводит к изменению подписи, что делает подделку обнаружимой.

В отличие от обычных хеш-функций, HMAC защищён от атак с подбором коллизий и расширения сообщения благодаря использованию секретного ключа.


Базовая модель угроз

Рассматривается типичная ситуация:

  • существует канал передачи данных без шифрования или с возможностью вмешательства
  • злоумышленник может изменить сообщение
  • злоумышленник не знает секретного ключа

Цель HMAC — гарантировать:

  • неизменность данных
  • невозможность незаметной подмены
  • проверку подлинности отправителя (при условии защиты ключа)

Поддержка HMAC в Web Crypto API

Работа с HMAC в браузере осуществляется через crypto.subtle, где доступны операции:

  • генерация ключа
  • подпись данных
  • проверка подписи
  • импорт и экспорт ключей

Ключевой объект — CryptoKey, содержащий секретный ключ HMAC.


Генерация HMAC-ключа

Создание ключа выполняется с использованием generateKey.

const key = await crypto.subtle.generateKey(
  {
    name: "HMAC",
    hash: { name: "SHA-256" }
  },
  true,
  ["sign", "verify"]
);

Разбор параметров:

  • name: "HMAC" — использование алгоритма HMAC
  • hash: { name: "SHA-256" } — хеш-функция внутри HMAC
  • true — возможность экспорта ключа
  • ["sign", "verify"] — назначение ключа

Использование SHA-256 является наиболее распространённым вариантом, обеспечивающим баланс между скоростью и криптографической стойкостью.


Подготовка данных для подписи

Web Crypto API работает с бинарными данными. Строки необходимо преобразовывать в ArrayBuffer.

function encode(data) {
  return new TextEncoder().encode(data);
}

Создание HMAC-подписи

Подпись формируется методом subtle.sign.

const data = encode("сообщение для защиты");

const signature = await crypto.subtle.sign(
  "HMAC",
  key,
  data
);

Результатом является ArrayBuffer, содержащий бинарную подпись.

Для удобства хранения или передачи часто используется преобразование в Base64:

function toBase64(buffer) {
  return btoa(String.fromCharCode(...new Uint8Array(buffer)));
}

Проверка подписи

Проверка выполняется методом subtle.verify.

const isValid = await crypto.subtle.verify(
  "HMAC",
  key,
  signature,
  data
);

Результат:

  • true — данные не изменены и подпись корректна
  • false — данные или подпись подделаны

Важно, что проверка выполняется на уровне Web Crypto API и устойчива к тайминговым атакам, так как сравнение подписи происходит в постоянное время.


Практический пример полного цикла

function encode(data) {
  return new TextEncoder().encode(data);
}

function toBase64(buffer) {
  return btoa(String.fromCharCode(...new Uint8Array(buffer)));
}

function fromBase64(base64) {
  const binary = atob(base64);
  return Uint8Array.from(binary, c => c.charCodeAt(0));
}

const key = await crypto.subtle.generateKey(
  {
    name: "HMAC",
    hash: { name: "SHA-256" }
  },
  true,
  ["sign", "verify"]
);

const message = "важные данные";
const encoded = encode(message);

const signatureBuffer = await crypto.subtle.sign(
  "HMAC",
  key,
  encoded
);

const signatureBase64 = toBase64(signatureBuffer);

const receivedData = encode(message);
const receivedSignature = fromBase64(signatureBase64);

const valid = await crypto.subtle.verify(
  "HMAC",
  key,
  receivedSignature,
  receivedData
);

Импорт и экспорт ключей

Для хранения или передачи ключ может быть экспортирован.

Экспорт:

const rawKey = await crypto.subtle.exportKey("raw", key);

Импорт:

const importedKey = await crypto.subtle.importKey(
  "raw",
  rawKey,
  {
    name: "HMAC",
    hash: { name: "SHA-256" }
  },
  true,
  ["sign", "verify"]
);

Экспорт в формате raw представляет собой бинарный массив секретного ключа.


Выбор хеш-функции

Web Crypto API поддерживает несколько вариантов хеша внутри HMAC:

  • SHA-1 (устаревший, не рекомендуется)
  • SHA-256 (стандартный выбор)
  • SHA-384
  • SHA-512

SHA-256 используется как базовый вариант в большинстве веб-приложений.


Особенности работы в браузере

HMAC в Web Crypto API имеет ряд особенностей:

  • ключ хранится в виде CryptoKey, недоступного напрямую в памяти JavaScript
  • операции выполняются асинхронно
  • доступ возможен только в безопасном контексте (HTTPS)
  • ключ нельзя извлечь, если он не был явно экспортирован

Типичные ошибки при использовании HMAC

1. Использование строк вместо бинарных данных

API не принимает строки напрямую. Любые данные должны быть преобразованы в ArrayBuffer.


2. Повторное использование ключей без контроля

Один и тот же ключ не должен использоваться в разных контекстах без разделения области применения. В противном случае возможна утечка безопасности между подсистемами.


3. Хранение ключа в открытом виде

Экспортированный ключ в формате raw должен храниться с тем же уровнем защиты, что и пароли или токены.


4. Игнорирование проверки результата verify

Метод verify возвращает логическое значение, и его необходимо использовать как единственный источник истины при проверке целостности данных.


Отличие HMAC от цифровой подписи

HMAC часто путают с асимметричной подписью, но архитектурно они различаются:

  • HMAC использует один секретный ключ
  • цифровая подпись использует пару ключей (private/public)
  • HMAC быстрее и проще
  • цифровая подпись обеспечивает возможность публичной проверки без секретного ключа

HMAC применяется там, где обе стороны доверяют общему секрету.


Производительность и ограничения

Web Crypto API реализует HMAC на уровне нативных криптографических библиотек браузера, что обеспечивает:

  • высокую скорость обработки
  • отсутствие утечек памяти через JavaScript
  • устойчивость к большинству атак на реализацию

Ограничением является невозможность синхронного выполнения операций — все методы возвращают Promise.


Сценарии применения

HMAC используется для:

  • защиты API-запросов
  • проверки целостности cookie и токенов
  • защиты локальных данных от изменения
  • подписания сообщений между сервисами
  • контроля целостности WebSocket-сообщений

Формирование безопасного протокола передачи данных

Типичный поток работы:

  1. генерация общего секретного ключа
  2. подпись исходного сообщения
  3. передача сообщения и подписи
  4. проверка подписи на стороне получателя
  5. отклонение сообщения при несовпадении

Поведение при подделке данных

Любое изменение:

  • текста сообщения
  • порядка байтов
  • кодировки

приводит к изменению HMAC. Проверка завершится отрицательным результатом без указания причины, что предотвращает утечку информации о структуре ключа.


Закрепление модели безопасности

HMAC в Web Crypto API обеспечивает криптографическую целостность данных при условии:

  • сохранности секретного ключа
  • корректной обработки бинарных данных
  • строгой проверки результатов операций

Механизм остаётся устойчивым к подделке сообщений при нарушении канала передачи, сохраняя целостность и подлинность информации на уровне криптографического протокола браузера.