Каждый входящий запрос содержит заголовок X-Webhook-Signature — это HMAC SHA-256 подпись тела запроса, созданная с помощью вашего секретного ключа.
Зачем проверять: без этого любой может отправить запрос на ваш сервер и притвориться Sasha AI.
Заголовки запросов
| Заголовок | Что содержит |
|---|
Content-Type | application/json |
X-Webhook-Signature | HMAC SHA-256 подпись тела запроса |
X-Webhook-ID | Уникальный ID события |
X-Webhook-Timestamp | Время отправки, ISO 8601 |
X-Call-List-ID | ID колл-листа |
Алгоритм проверки
Возьмите тело запроса как строку
Получите исходное тело запроса до парсинга JSON
Посчитайте HMAC SHA-256
Вычислите HMAC SHA-256 с вашим секретным ключом, результат — hex-строка
Сравните с заголовком
Сравните вычисленную подпись с полученной в заголовке X-Webhook-Signature, используя безопасное сравнение строк
Примеры кода
const crypto = require('crypto');
function verifyWebhookSignature(payload, signature, secret) {
const computed = crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(computed, 'hex'),
Buffer.from(signature, 'hex')
);
}
// Пример использования в Express
app.post('/webhook', express.text({ type: 'application/json' }), (req, res) => {
const signature = req.headers['x-webhook-signature'];
const payload = req.body; // Сырое тело запроса в виде строки
const secret = 'ваш_секретный_ключ_вебхука';
if (!verifyWebhookSignature(payload, signature, secret)) {
return res.status(401).send('Недействительная подпись');
}
const data = JSON.parse(payload);
console.log('Получено событие:', data.type);
res.status(200).send('OK');
});
Используйте сравнение строк, устойчивое к time-based атакам (например, crypto.timingSafeEqual() в Node.js, hmac.compare_digest() в Python или hash_equals() в PHP).