Вебхуки: подписанные данные и стратегии повтора
Вы только что создали конечную точку вебхука для получения событий от стороннего сервиса. Все работает при тестировании, но в производственной среде вы замечаете, что некоторые события не приходят, или, что еще хуже, вы получаете дублирующиеся события, вызывающие несоответствия в данных. Это классические ловушки вебхуков, которые проистекают из двух критических аспектов: подлинности данных и надежности доставки.
Вебхуки — это HTTP-обратные вызовы, инициируемые событиями, которые передают данные в ваше приложение в режиме реального времени. В отличие от опроса, они эффективны и почти мгновенны. Но они создают проблемы: как узнать, что запрос действительно пришел от ожидаемого отправителя, и как обрабатывать сбои, когда получатель временно недоступен? В этой статье рассматриваются подписанные данные и механизмы повтора, даются практические шаблоны для создания устойчивых интеграций вебхуков.
Почему вебхукам нужны подписи
Конечные точки вебхуков — это общедоступные URL-адреса. Без проверки любой может отправлять поддельные запросы на вашу конечную точку, потенциально вызывая несанкционированные действия или повреждение данных. Подписанные данные решают эту проблему, добавляя криптографическую подпись к каждому запросу, что позволяет вам проверить личность отправителя и убедиться, что данные не были изменены.
Большинство провайдеров используют HMAC (код аутентификации сообщения на основе хэша) с общим секретом. Отправитель вычисляет хэш данных с использованием секрета и включает его в заголовок. Вы пересчитываете хэш на своей стороне и сравниваете. Если они совпадают, запрос является законным.
Реализация проверки HMAC
Давайте рассмотрим типичную реализацию на Node.js. Предположим, провайдер отправляет подпись в заголовке X-Signature, вычисленную как HMAC-SHA256 от необработанного тела запроса с использованием вашего секрета.
const crypto = require('crypto');
const express = require('express');
const app = express();
// Use raw body parser to preserve exact payload
app.use(express.raw({ type: 'application/json' }));
app.post('/webhook', (req, res) => {
const signature = req.headers['x-signature'];
const secret = process.env.WEBHOOK_SECRET;
const expected = crypto
.createHmac('sha256', secret)
.update(req.body)
.digest('hex');
if (!crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected))) {
return res.status(401).send('Invalid signature');
}
// Process the event
const event = JSON.parse(req.body);
// ... handle event
res.status(200).send('OK');
});
Ключевые моменты:
- Всегда используйте необработанное тело запроса для проверки, а не проанализированный JSON. Анализ может изменить пробелы или порядок ключей, нарушив хэш.
- Используйте сравнение, устойчивое к временным атакам.
- Храните свой секрет в безопасности; периодически меняйте его и поддерживайте несколько активных секретов во время ротации.
Разработка стратегии повтора
Даже с подписями доставка может не удаться из-за проблем с сетью, простоя сервера или тайм-аутов. Надежная стратегия повтора обеспечивает eventual delivery, не перегружая вашу систему или отправителя.
1. Быстро подтверждайте, обрабатывайте асинхронно
Ваша конечная точка вебхука должна как можно быстрее ответить статусом 2xx, в идеале в течение нескольких секунд. Если обработка занимает больше времени, поставьте событие в очередь для фоновой обработки. Это предотвращает тайм-ауты и позволяет отправителю считать доставку успешной.
2. Реализуйте экспоненциальную задержку с джиттером
Когда доставка не удалась (ответ не 2xx или тайм-аут), отправитель должен повторить попытку с увеличивающимися задержками. Экспоненциальная задержка (например, 1 с, 2 с, 4 с, 8 с...) предотвращает перегрузку вашей конечной точки. Добавление случайного джиттера избегает проблемы «стада» при одновременном сбое многих вебхуков.
Пример графика повторов:
- Попытка 1: немедленно
- Попытка 2: через 30 секунд
- Попытка 3: через 2 минуты
- Попытка 4: через 10 минут
- Попытка 5: через 1 час
После максимального количества повторов (например, 5) отправитель должен остановиться и уведомить вас (например, по электронной почте или через панель управления).
3. Обрабатывайте дубликаты с помощью идемпотентности
Повторы могут вызвать дублирующиеся доставки. Ваша конечная точка должна быть идемпотентной: обработка одного и того же события несколько раз должна иметь тот же эффект, что и однократная обработка. Используйте уникальный идентификатор события из данных и сохраните его. Перед обработкой проверьте, не встречался ли этот идентификатор; если да, верните 200 без повторной обработки.
Распространенные подходы:
- Уникальное ограничение базы данных на идентификатор события.
- Набор Redis с TTL для недавно обработанных идентификаторов.
- Ключи идемпотентности в вашей бизнес-логике.
Сравнение: подписанные данные и другие меры безопасности
| Метод | Плюсы | Минусы |
|---|---|---|
| Подпись HMAC | Проверяет подлинность и целостность; без сохранения состояния | Требует управления общим секретом |
| Белый список IP | Просто реализовать | IP-адреса могут меняться; не подходит для динамических сред |
| Базовая аутентификация | Легко добавить | Учетные данные отправляются в каждом запросе; менее безопасно |
| JWT в заголовке | Может включать утверждения и срок действия | Накладные расходы на выпуск/проверку токена |
Подписи HMAC являются де-факто стандартом, потому что они не сохраняют состояние, эффективны и обеспечивают надежную безопасность при правильной реализации.
Лучшие практики для потребителей вебхуков
- Проверяйте подписи при каждом запросе, даже при разработке.
- Отвечайте быстро; перекладывайте тяжелую обработку в очередь.
- Регистрируйте все — заголовки, данные и результат обработки — для отладки.
- Отслеживайте сбои и настройте оповещения о повторяющихся ошибках.
- Используйте HTTPS для защиты данных при передаче.
- Регулярно меняйте секреты и поддерживайте несколько активных секретов во время ротации.
Лучшие практики для производителей вебхуков
- Подписывайте данные секретом, уникальным для каждого потребителя.
- Включайте уникальный идентификатор события для облегчения идемпотентности.
- Повторяйте с задержкой и ограничивайте количество попыток.
- Предоставьте панель управления для потребителей, чтобы просматривать журналы доставки и повторять события.
- Четко документируйте вашу схему подписи.
Тестирование вашей реализации вебхука
Используйте такие инструменты, как ngrok, чтобы открыть ваш локальный сервер и имитировать доставку вебхуков. Напишите модульные тесты для проверки подписи и логики идемпотентности. Для нагрузочного тестирования рассмотрите инструменты, которые могут отправлять подписанные запросы в масштабе.
При отладке проверяйте необработанные данные и заголовки. Форматировщик JSON может помочь визуализировать структуру данных. Попробуйте наш JSON Formatter, чтобы быстро отформатировать и проверить данные вебхука.
Часто задаваемые вопросы
Что такое подписанные данные вебхука?
Подписанные данные включают криптографическую подпись (обычно HMAC), которая позволяет получателю проверить личность отправителя и убедиться, что данные не были изменены. Она вычисляется с использованием общего секрета и тела запроса.
Сколько раз следует повторять вебхук?
Универсального числа нет, но распространенная практика — 3–5 повторов с экспоненциальной задержкой. После этого событие считается неудачным, и потребитель должен быть уведомлен для расследования или ручного повтора.
Могу ли я использовать JWT вместо HMAC для подписей вебхуков?
Да, JWT может работать, но это добавляет сложности (выпуск токена, срок действия). HMAC проще и достаточно для большинства случаев. Выбирайте в зависимости от вашей существующей инфраструктуры и требований.
Готовы проверять и отлаживать данные вебхуков? Используйте наш бесплатный JSON Formatter, чтобы мгновенно форматировать и проверять JSON.