Webhooks: подписанные payload и стратегии повтора
Почему webhooks дают сбои и как это исправить
Вы только что интегрировали webhook платёжного провайдера для обновления статусов заказов в вашем приложении. В тестах всё работает, но в продакшене некоторые события не приходят или, что хуже, вы получаете дубликаты. Без надлежащих мер защиты webhooks могут стать источником скрытых сбоев и уязвимостей.
Webhooks — это HTTP-колбэки, запускаемые событиями, но, в отличие от типичных вызовов API, они работают по принципу push и асинхронно. Это создаёт две основные проблемы: проверка подлинности и обеспечение доставки. В этом руководстве мы рассмотрим обе с помощью подписанных payload и продуманных повторных попыток.
Что такое webhooks?
Webhook — это определяемый пользователем HTTP-колбэк. Когда в исходной системе происходит событие (например, новый платёж), она отправляет HTTP POST-запрос на указанный вами URL с данными события. Это позволяет реагировать в реальном времени без опроса.
Типичные сценарии использования включают уведомления о платежах, триггеры CI/CD и интеграции с чатами. Но поскольку webhooks передаются через публичный интернет, они уязвимы к подделке и потере.
Защита webhooks с помощью подписанных payload
Как узнать, что webhook действительно пришёл от вашего провайдера? Подписание payload общим секретом обеспечивает целостность и подлинность.
Как работают HMAC-подписи
Провайдер вычисляет HMAC (например, SHA-256) тела запроса с использованием секретного ключа и отправляет подпись в заголовке (например, X-Signature). Ваш сервер пересчитывает HMAC и сравнивает его с помощью сравнения за постоянное время, чтобы предотвратить атаки по времени.
// Пример на Node.js: проверка подписи webhook
const crypto = require('crypto');
function verifySignature(payload, signature, secret) {
const hmac = crypto.createHmac('sha256', secret);
hmac.update(payload);
const digest = hmac.digest('hex');
return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(digest));
}
Лучшие практики:
- Используйте надёжный уникальный секрет для каждой конечной точки webhook.
- Включайте временную метку в подписанный payload для предотвращения атак повторного воспроизведения; отклоняйте запросы старше нескольких минут.
- Всегда используйте HTTPS для защиты payload при передаче.
Реализация надёжных повторных попыток
Даже с подписями сетевые проблемы или простои могут привести к сбою доставки webhook. Провайдеры обычно повторяют попытки с экспоненциальной задержкой, но вы также должны обрабатывать повторы на своей стороне.
Идемпотентность: ключ к безопасным повторам
Повторы могут привести к дублирующей обработке. Используйте ключи идемпотентности (часто включаются в payload или в заголовок), чтобы гарантировать, что каждое событие обрабатывается ровно один раз. Сохраняйте идентификаторы обработанных событий и игнорируйте дубликаты.
// Пример: идемпотентный обработчик webhook
const processedEvents = new Set();
app.post('/webhook', (req, res) => {
const eventId = req.body.id;
if (processedEvents.has(eventId)) {
return res.status(200).send('Already processed');
}
// process event...
processedEvents.add(eventId);
res.status(200).send('OK');
});
Стратегия повторов на стороне получателя
Если обработка не удалась, ответьте статусом 5xx, чтобы инициировать повторную попытку провайдера. Для временных ошибок вы также можете реализовать собственную очередь повторов с задержкой.
| Сценарий | Ответ | Действие |
|---|---|---|
| Успех | 200 OK | Без повтора |
| Ошибка валидации | 400 Bad Request | Без повтора (исправьте payload) |
| Ошибка сервера | 500 Internal Server Error | Провайдер повторяет |
| Ограничение скорости | 429 Too Many Requests | Провайдер повторяет после задержки |
Мониторинг и отладка webhooks
Записывайте все входящие запросы webhook с заголовками и payload (скрывая конфиденциальные данные). Используйте инструмент, такой как JSON Formatter, для форматирования payload и облегчения проверки. Настройте оповещения о неудачных доставках и отслеживайте количество повторов.
FAQ
Что такое подписанный payload webhook?
Подписанный payload включает криптографическую подпись (обычно HMAC), которая подтверждает, что запрос пришёл из доверенного источника и не был изменён.
Сколько раз следует повторять неудачный webhook?
Большинство провайдеров повторяют 3–5 раз с экспоненциальной задержкой. Как получатель, отвечайте соответствующими кодами состояния и позволяйте провайдеру обрабатывать повторы.
Можно ли использовать webhooks без HTTPS?
Нет — HTTPS необходим для шифрования payload и предотвращения атак «человек посередине». Никогда не принимайте webhooks по простому HTTP.
Готовы проверять и отлаживать payload webhooks? Попробуйте наш бесплатный JSON Formatter, чтобы мгновенно форматировать и проверять данные JSON.