HMAC (Hash-based Message Authentication Code) используется в API для подтверждения целостности запроса и проверки подлинности отправителя. В основе лежит криптографическая хеш-функция и секретный ключ, известный только клиенту и серверу.
Ключевая особенность HMAC — невозможность подделки подписи без знания секретного ключа, даже если злоумышленнику известен сам алгоритм и передаваемые данные.
HMAC формируется по следующей логике:
Формально:
(K, m) = H((K opad) ;||; H((K ipad) ;||; m))
Где:
В API HMAC используется для:
Типичный сценарий:
Библиотека CryptoJS предоставляет удобные функции для генерации HMAC с различными алгоритмами.
Поддерживаемые варианты:
На практике в API чаще используется HMAC-SHA256.
import CryptoJS from "crypto-js";
или через CDN:
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.2.0/crypto-js.min.js"></script>
const secretKey = "my_super_secret_key";
const message = "user_id=42&action=transfer";
const signature = CryptoJS.HmacSHA256(message, secretKey).toString();
console.log(signature);
Результат — строка в шестнадцатеричном формате, которая используется как подпись запроса.
В реальных API подпись формируется не из произвольной строки, а из строго определённого канонического запроса.
Пример структуры:
const method = "POST";
const endpoint = "/api/v1/transfer";
const timestamp = Date.now();
const body = JSON.stringify({
from: "A1",
to: "B2",
amount: 500
});
const payload = `${method}\n${endpoint}\n${timestamp}\n${body}`;
const secretKey = "super_secret_key";
const signature = CryptoJS
.HmacSHA256(payload, secretKey)
.toString(CryptoJS.enc.Hex);
Обычно подпись передаётся в заголовках:
fetch("https://api.example.com/api/v1/transfer", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-TIMESTAMP": timestamp,
"X-API-SIGNATURE": signature
},
body: body
});
Сервер выполняет идентичные действия:
Псевдологика проверки:
if (serverSignature === clientSignature) {
// запрос валиден
} else {
// отклонить запрос
}
Timestamp защищает от повторной отправки запроса.
Пример:
const timestamp = Date.now();
Сервер проверяет допустимое окно времени (например, ±5 минут).
Без timestamp и уникальных параметров запрос можно перехватить и повторить.
Механизмы защиты:
const nonce = CryptoJS.lib.WordArray.random(16).toString();
Добавляется в подпись:
const payload = `${method}\n${endpoint}\n${timestamp}\n${nonce}\n${body}`;
Разный порядок параметров приводит к разным подписям.
JSON с пробелами и без — это разные строки.
Base64 vs Hex может привести к несовпадению подписи.
CryptoJS.HmacSHA256(payload, key).toString(CryptoJS.enc.Base64);
import CryptoJS from "crypto-js";
const API_SECRET = "secret";
const API_KEY = "public_key";
function signRequest(method, url, body = "") {
const timestamp = Date.now();
const payload = `${method}\n${url}\n${timestamp}\n${body}`;
const signature = CryptoJS
.HmacSHA256(payload, API_SECRET)
.toString(CryptoJS.enc.Hex);
return {
timestamp,
signature
};
}
async function request() {
const method = "POST";
const url = "/api/v1/transfer";
const body = JSON.stringify({ amount: 100 });
const { timestamp, signature } = signRequest(method, url, body);
return fetch(url, {
method,
headers: {
"X-API-KEY": API_KEY,
"X-API-TIMESTAMP": timestamp,
"X-API-SIGNATURE": signature,
"Content-Type": "application/json"
},
body
});
}
Безопасность HMAC основана на свойствах:
HMAC используется в:
HMAC:
JWT:
CryptoJS реализован на JavaScript и подходит для:
Для серверных high-load систем чаще используют нативные крипто-библиотеки Node.js.