Webhooks: подписанные payloads и надёжные повторы
Вы только что интегрировали сторонний платёжный шлюз или CI/CD-сервис, и теперь вам нужны уведомления в реальном времени. Webhooks — это решение, но без должной безопасности и надёжности они могут стать источником разочарования. Злоумышленники могут подделывать запросы, а сетевые сбои — приводить к пропуску событий. В этом руководстве мы разберём, как защитить webhooks с помощью подписанных payloads и реализовать надёжные механизмы повторов.
Что такое webhooks?
Webhooks — это определяемые пользователем HTTP-callbacks, запускаемые по определённым событиям. Когда происходит событие (например, успешный платёж), сервис-источник отправляет HTTP POST-запрос на указанный вами URL с данными события. Эта push-модель эффективнее polling и позволяет реагировать в реальном времени.
Однако webhooks создают две проблемы: подлинность (убедиться, что запрос пришёл от ожидаемого отправителя) и надёжность (убедиться, что вы не пропустили события).
Защита webhooks с помощью подписанных payloads
Подписанный payload использует секрет, общий для отправителя и получателя, для создания криптографической подписи. Отправитель включает эту подпись в заголовок, а получатель проверяет её, чтобы подтвердить целостность и происхождение payload.
Как работает HMAC-подпись
HMAC (Hash-based Message Authentication Code) — стандартный подход. Отправитель вычисляет HMAC над телом запроса с использованием общего секрета и включает его в заголовок, например X-Signature. Получатель пересчитывает HMAC и сравнивает.
// Пример: генерация подписи в Node.js
const crypto = require('crypto');
function generateSignature(payload, secret) {
return crypto
.createHmac('sha256', secret)
.update(JSON.stringify(payload))
.digest('hex');
}
// На принимающей стороне проверяем:
function verifySignature(payload, secret, receivedSignature) {
const expected = generateSignature(payload, secret);
// Используйте сравнение, устойчивое к тайминговым атакам
return crypto.timingSafeEqual(
Buffer.from(expected, 'hex'),
Buffer.from(receivedSignature, 'hex')
);
}
Ключевые моменты:
- Всегда используйте надёжный уникальный секрет для каждой конечной точки webhook.
- Сравнивайте подписи с помощью функции, устойчивой к тайминговым атакам, чтобы избежать утечки информации.
- Включайте временную метку в подпись для предотвращения replay-атак (например, подписывайте
timestamp + payload).
Дополнительные меры безопасности
- HTTPS: всегда используйте HTTPS для шифрования данных при передаче.
- Allowlisting IP: если провайдер публикует диапазоны IP, ограничьте входящие запросы этими IP.
- Ограничения размера payload: отклоняйте слишком большие payloads для снижения риска DoS.
Реализация надёжных повторов
Сети ненадёжны. Webhook может не доставиться из-за таймаутов, ошибок сервера или временных сбоев. Надёжная стратегия повторов обеспечивает итоговую доставку, не перегружая вашу систему или отправителя.
Лучшие практики повторов
- Экспоненциальная задержка: увеличивайте интервал между повторами (например, 1с, 2с, 4с, 8с...).
- Максимальный лимит повторов: ограничьте число попыток (например, 5), чтобы избежать бесконечных циклов.
- Dead-letter queue: после исчерпания повторов сохраните неудачные события для ручной проверки.
- Идемпотентность: убедитесь, что обработка одного и того же события несколько раз не вызывает дублирующих побочных эффектов.
Идемпотентность на практике
Включайте уникальный ID события в payload. На стороне получателя отслеживайте обработанные ID (в базе данных или кэше) и игнорируйте дубликаты. Например:
// Псевдокод для идемпотентной обработки
if (eventStore.exists(event.id)) {
return 200; // Уже обработано
}
// Обработка события...
eventStore.save(event.id);
Корректная обработка сбоев
Быстро отвечайте статусом 2xx, чтобы подтвердить получение. Если обработка занимает время, поставьте событие в очередь и обрабатывайте асинхронно. Возвращайте 5xx только для временных ошибок, требующих повтора; для постоянных ошибок (например, неверный payload) возвращайте 4xx, чтобы предотвратить бесполезные повторы.
Сравнение: push vs. poll
| Аспект | Webhooks (Push) | Polling |
|---|---|---|
| Задержка | Почти реальное время | Зависит от интервала |
| Эффективность | Отправка только при событиях | Постоянные запросы |
| Сложность | Требует логики повторов/безопасности | Проще, но расточительно |
Тестирование реализации webhook
Используйте инструменты вроде ngrok, чтобы открыть локальный сервер и имитировать доставку webhooks. Проверяйте валидацию подписи и поведение повторов, намеренно вызывая сбои запросов.
FAQ
Что такое подписанный payload webhook?
Подписанный payload включает криптографическую подпись (часто HMAC), которая позволяет получателю проверить подлинность и целостность запроса с помощью общего секрета.
Сколько раз нужно повторять неудачный webhook?
Универсального числа нет, но обычно используют 3–5 повторов с экспоненциальной задержкой. После этого зафиксируйте сбой для ручной проверки.
Можно ли использовать webhooks без HTTPS?
Технически да, но это крайне небезопасно. Всегда используйте HTTPS, чтобы предотвратить прослушивание и атаки man-in-the-middle.
Готовы проверить payloads webhooks? Используйте наш JSON formatter, чтобы быстро отформатировать и провалидировать JSON payloads.