HMAC: аутентификация сообщений
HMAC (Hash-based Message Authentication Code), использование в API, подписи запросов, безопасность.
Введение
Представьте, что вы получаете webhook от платёжной системы: «заказ оплачен, отправляйте товар». Как убедиться, что сообщение действительно пришло от платёжки, а не от подделывающего её атакующего? Здесь на сцену выходит HMAC — алгоритм, который позволяет проверить подлинность сообщения с помощью секретного ключа. В статье разберёмся, как устроен HMAC, где он применяется, чем отличается от обычного хеширования и какие ошибки совершают разработчики при его использовании. Попробовать HMAC в действии можно в нашемHMAC-генераторе.
Что такое HMAC
HMAC (Hash-based Message Authentication Code) — это механизм, который combines хеш-функцию и секретный ключ для создания кода аутентификации сообщения. В отличие от обычного хеша, HMAC невозможно подделать без знания ключа: даже если атакующий перехватит сообщение и заменит его, он не сможет пересчитать корректный MAC.
Формально HMAC вычисляется по формуле:
HMAC(K, m) = H((K' ⊕ opad) ‖ H((K' ⊕ ipad) ‖ m))
где:
H — хеш-функция (например, SHA-256)
K — секретный ключ
K' — ключ, дополненный до размера блока хеша
m — сообщение
‖ — конкатенация
⊕ — XOR
opad = 0x5c5c...5c (внешний padding)
ipad = 0x3636...36 (внутренний padding)Двойное хеширование с разными паддингами защищает от расширений длины — атаки, применимой к обычным хешам типа SHA-256.
HMAC vs обычный хеш
| Свойство | SHA-256 | HMAC-SHA256 |
|---|---|---|
| Требует ключа | Нет | Да |
| Проверка целостности | Да | Да |
| Аутентификация отправителя | Нет | Да |
| Защита от подделки | Нет | Да |
| Защита от расширения длины | Нет | Да |
Если просто приложить ключ к сообщению и захешировать (SHA256(key + msg)), атакующий может дописать к сообщению произвольные данные и пересчитать хеш с помощью атаки на расширение длины. HMAC устроен так, что эта атака не работает.
Где применяется HMAC
1. Подпись API-запросов
Многие REST API (AWS, Telegram Bot API, Stripe) требуют, чтобы каждый запрос содержал HMAC-подпись. Клиент вычисляет HMAC от канонического представления запроса (метод + путь + тело + таймстамп) с помощью секретного ключа и отправляет подпись в заголовке. Сервер проверяет подпись и принимает решение.
// Пример подписи API-запроса (Node.js)
const crypto = require('crypto');
const apiKey = 'client-id-123';
const apiSecret = 'top-secret-key';
const timestamp = Date.now().toString();
const method = 'POST';
const path = '/api/v1/orders';
const body = JSON.stringify({ item: 'A1', qty: 2 });
const message = [timestamp, method, path, body].join('\n');
const signature = crypto
.createHmac('sha256', apiSecret)
.update(message)
.digest('hex');
const response = await fetch('https://api.example.com' + path, {
method,
headers: {
'X-API-Key': apiKey,
'X-Timestamp': timestamp,
'X-Signature': signature,
'Content-Type': 'application/json',
},
body,
});2. Webhook-подписи
Когда сервис (Stripe, GitHub, Tinkoff) отправляет вам webhook, он прикладывает HMAC-подпись тела запроса. Ваш сервер вычисляет HMAC с тем же секретом и сверяет с присланным значением. Если подпись не совпала — запрос отбрасывается.
3. JWT-токены
JSON Web Token в режиме HS256 подписывается с помощью HMAC-SHA256. Заголовок и payload кодируются в base64url, склеиваются через точку, и к ним добавляется HMAC-подпись. Подробнее — в нашей статье проJWT-токены.
4. HOTP и TOTP
Одноразовые пароли из приложений-аутентификаторов (Google Authenticator, Authy) строятся на HMAC. HOTP использует счётчик, TOTP — текущее время. Секретный ключ известен серверу и приложению, но не передаётся по сети.
5. TLS 1.2 и 1.3
Протокол TLS использует HMAC для защиты записей. В TLS 1.3 HMAC применяется в составе HKDF (HMAC-based Key Derivation Function) для порождения ключей.
6. Хранение паролей (PBKDF2)
Функция PBKDF2 построена на многократном применении HMAC. Она используется для безопасного получения ключа из пароля. Хотя для новых проектов лучше выбратьbcrypt или argon2, PBKDF2 всё ещё встречается в WPA2, 1Password и других системах.
Выбор хеш-функции
HMAC может работать с любой криптографической хеш-функцией. На практике встречаются:
- HMAC-SHA256 — самый популярный, рекомендуется по умолчанию.
- HMAC-SHA512 — для длинных ключей, чуть медленнее.
- HMAC-SHA1 — устаревший, встречается в legacy-API.
- HMAC-MD5 — нельзя использовать в новых проектах.
- HMAC-SHA3-256 — перспективный, но пока редкий.
Безопасность HMAC не падает до ноль, даже если хеш-функция имеет известные коллизии (как MD5). Тем не менее, для новых систем рекомендуется SHA-256 или SHA-3. Подробнее о выборе хеша — в статье«Сравнение хеш-функций».
Сравнение подписей
# HMAC-SHA256 для "hello" с ключом "secret"
$ echo -n "hello" | openssl dgst -sha256 -hmac "secret"
HMAC-SHA256(stdin)= 88aab3ede8d3adf94d26ab90d3bafd4a2083070c3bcce9c014ee04a443847c0b
# HMAC-SHA512 для того же сообщения
$ echo -n "hello" | openssl dgst -sha512 -hmac "secret"
HMAC-SHA512(stdin)= 9b6ec8dd31b87f6f8e13... (128 hex символов)Безопасное сравнение подписей
При проверке HMAC критически важно использовать сравнение за константное время. Обычный оператор == завершается при первом отличающемся байте, что позволяет атакующему по времени ответа восстанавливать подпись побайтово — это так называемая timing attack.
// ❌ Плохо — уязвимо к timing attack
if (receivedSignature === expectedSignature) { ... }
// ✅ Хорошо — константное время сравнения
const crypto = require('crypto');
const a = Buffer.from(receivedSignature, 'hex');
const b = Buffer.from(expectedSignature, 'hex');
if (a.length === b.length && crypto.timingSafeEqual(a, b)) { ... }
// Python
import hmac
if hmac.compare_digest(received, expected): ...Типичные ошибки
1. Слабый ключ
Если ключом служит короткая строка вроде «secret» или «1234», атакующий может перебрать его по словарю. Используйте криптостойкие ключи длиной не менее 256 бит.
2. Передача ключа в URL
Никогда не кладите секрет в query-string — он попадёт в логи прокси и браузера. Только в заголовок, и только по HTTPS.
3. Отсутствие таймстампа
Без таймстампа в подписываемом сообщении атакующий может бесконечно переиспользовать перехваченный запрос (replay attack). Добавляйте временную метку и проверяйте, что она отличается от текущего времени не более чем на 5 минут.
4. Использование одной и той же подписи для разных API
Каждое интеграционное подключение должно иметь свой секретный ключ. Если один ключ утёк — страдает только одна интеграция, а не все.
5. Хранение ключа в коде
Секреты не должны попадать в репозиторий. Используйте переменные окружения, secrets-менеджеры (Vault, AWS Secrets Manager) или Kubernetes Secrets.
Ротация ключей
Хорошая практика — периодически менять секретные ключи. При ротации:
- Заводите новый ключ, помечаете его как активный.
- Старый ключ остаётся валидным ещё 7–30 дней — для graceful-перехода.
- Через заданный срок старый ключ отключается.
- Все новые запросы подписываются новым ключом.
Многие API поддерживают передачу идентификатора ключа в заголовке, чтобы сервер знал, какой секрет использовать для проверки.
HMAC в разных языках
# Python
import hmac, hashlib
sig = hmac.new(b'secret', b'hello', hashlib.sha256).hexdigest()
# Go
import "crypto/hmac"
import "crypto/sha256"
h := hmac.New(sha256.New, []byte("secret"))
h.Write([]byte("hello"))
sig := hex.EncodeToString(h.Sum(nil))
# PHP
$sig = hash_hmac('sha256', 'hello', 'secret');Заключение
HMAC — простой и надёжный способ аутентифицировать сообщение. Он защищает от подделки, replay-атак (в связке с таймстампом) и расширений длины. Если вы делаете API, принимаете webhook или подписываете токены — HMAC практически всегда будет правильным выбором. Главное: используйте криптостойкие ключи, сравнивайте подписи за константное время и не кладите секреты в код. Сгенерировать HMAC-подпись для теста можно в нашемHMAC-генераторе.
Попробуйте эти инструменты
Похожие статьи
Как создать надёжный пароль: генератор паролей
Правила создания паролей, длина, символы, энтропия, менеджеры паролей, двухфакторная аутентификация.
MD5 хеш: что это и безопасно ли использовать
Алгоритм MD5, хеширование, коллизии, почему MD5 не для паролей, где ещё можно использовать.
SHA-256: безопасное хеширование
Алгоритм SHA-256, применение в блокчейне, SSL, цифровые подписи, сравнение с MD5 и SHA-1.
UUID: генерация уникальных идентификаторов
UUID версии 1-5, GUID, использование в базах данных, распределённых системах, генерация.