Вебхуки: подписанные данные и повторные попытки

Backend2026-10-26TryQuickToolBox

Зачем вебхукам нужны подписанные данные и повторные попытки

Вы только что интегрировали сторонний сервис, который отправляет вебхуки на ваш эндпоинт. В тестах всё работает, но в продакшене вы замечаете пропущенные события, дублирующиеся доставки и, что хуже, — кто-то подделывает запросы на ваш URL вебхука. Без надлежащих мер безопасности и надёжности вебхуки могут стать источником багов и уязвимостей.

Вебхуки — это HTTP-колбэки, которые уведомляют ваше приложение о наступлении события. Концептуально они просты, но на практике сложны, поскольку пересекают границы доверия и сталкиваются с проблемами надёжности сети. В этом руководстве мы рассмотрим два критически важных аспекта: подписанные данные для проверки подлинности и повторные попытки для гарантии доставки. Мы разберём практические реализации, которые вы можете применить сразу.

Понимание подписанных данных

Подписанные данные — это способ отправителя доказать, что вебхук пришёл именно от него и не был изменён. Отправитель вычисляет криптографическую подпись над телом запроса (а иногда и над заголовками) с использованием общего секрета, затем включает эту подпись в заголовок. Ваш эндпоинт пересчитывает подпись и сравнивает её.

Наиболее распространённый алгоритм — HMAC-SHA256. Он быстрый, безопасный и широко поддерживается. Вот как это работает:

  1. Отправитель и получатель используют общий секретный ключ (настраивается в параметрах вебхука).
  2. Для каждого вебхука отправитель создаёт подпись, хешируя данные с секретом с помощью HMAC-SHA256.
  3. Подпись отправляется в заголовке, например X-Signature.
  4. Получатель вычисляет тот же HMAC и сравнивает его, используя сравнение с постоянным временем для предотвращения атак по времени.

Реализация проверки HMAC в Node.js

Рассмотрим конкретный пример. Предположим, отправитель включает подпись в X-Signature в виде hex-строки. Вот как проверить её в маршруте Express:

const crypto = require('crypto');
const express = require('express');
const app = express();

// Используйте сырое тело для проверки подписи
app.use('/webhook', 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');
  }

  // Обработка события
  const event = JSON.parse(req.body);
  console.log('Received event:', event.type);

  res.status(200).send('OK');
});

app.listen(3000);

Важно: Всегда используйте сырое тело запроса для вычисления подписи, а не распарсенный JSON. Парсинг и повторная сериализация могут изменить пробелы и порядок, нарушив подпись.

Защита от повторов с помощью временных меток

Одни подписи не защищают от атак повторного воспроизведения. Злоумышленник может перехватить валидный подписанный запрос и отправить его позже. Чтобы смягчить это, включите временную метку в подписанные данные или в заголовок. Например, добавьте заголовок X-Timestamp с Unix-временем. Получатель проверяет, что временная метка находится в пределах допустимого окна (например, 5 минут), и отклоняет более старые запросы.

Также включайте временную метку в вычисление подписи, чтобы предотвратить подмену. Отправитель вычисляет HMAC над timestamp + '.' + body. Получатель делает то же самое и сравнивает.

Реализация повторных попыток для надёжной доставки

Вебхуки передаются через публичный интернет, поэтому сбои неизбежны: сетевые таймауты, серверные ошибки (5xx) или временная недоступность вашего эндпоинта. Надёжная система вебхуков должна повторять неудачные доставки.

Стратегии повторных попыток

Распространённые стратегии повторных попыток включают:

Типичная политика может быть такой: до 5 повторных попыток с экспоненциальной задержкой и джиттером, начиная с 10 секунд. После этого доставка помечается как неудачная, и, возможно, отправляется оповещение.

Идемпотентность: обработка дубликатов

Повторные попытки могут привести к дублирующимся доставкам. Ваш эндпоинт должен быть идемпотентным — обработка одного и того же события дважды не должна вызывать побочных эффектов, например двойного списания средств с клиента. Включите уникальный ID события в данные (например, event.id) и сохраняйте обработанные ID в течение разумного периода. Перед обработкой проверьте, не встречался ли уже этот ID.

В качестве альтернативы проектируйте операции так, чтобы они были естественно идемпотентными (например, используя upsert вместо insert).

Быстрый ответ

Ваш эндпоинт вебхука должен быстро подтверждать получение (в течение нескольких секунд) и обрабатывать событие асинхронно. Если обработка занимает слишком много времени, отправитель может получить таймаут и повторить попытку, вызвав дубликаты. Используйте очередь: отвечайте 200 OK сразу после проверки подписи, затем ставьте событие в очередь для фоновой обработки.

Сравнение мер безопасности вебхуков

Мера Назначение Сложность реализации
Подпись HMAC Проверка подлинности и целостности Низкая
Проверка временной метки Защита от атак повторного воспроизведения Низкая
Ключи идемпотентности Предотвращение дублирующей обработки Средняя
Белый список IP Ограничение известными IP отправителя Низкая (но ненадёжная)
HTTPS Шифрование данных при передаче Низкая

Хотя белый список IP может добавить уровень защиты, он ненадёжен, поскольку IP отправителя могут меняться. Полагайтесь в первую очередь на подписи.

Тестирование эндпоинта вебхука

Используйте инструменты вроде ngrok, чтобы открыть локальный сервер и запускать тестовые вебхуки из панели провайдера. Убедитесь, что подписи проверяются корректно, и что повторные попытки работают, симулируя сбои (например, возвращайте 500 для первых нескольких попыток).

При отладке вам может потребоваться просмотреть JSON-данные. JSON Formatter поможет быстро отформатировать и проверить данные вебхука.

Часто задаваемые вопросы

Что такое подписанные данные вебхука?

Подписанные данные включают криптографическую подпись (обычно HMAC), которая позволяет получателю убедиться, что запрос пришёл от ожидаемого отправителя и не был изменён. Она вычисляется с использованием общего секрета.

Сколько раз повторять неудачный вебхук?

Универсального числа нет, но обычно делают 3–5 повторных попыток с экспоненциальной задержкой. После этого логируйте сбой и рассмотрите оповещение. Цель — обработать временные проблемы без бесконечных повторов.

Можно ли использовать JWT для подписи вебхуков?

Да, некоторые провайдеры используют JWT (JSON Web Tokens) для подписи вебхуков. Токен содержит утверждения и подписывается секретом или закрытым ключом. Проверка аналогична: проверьте подпись и утверждения, такие как срок действия.

Готовы внедрить безопасные вебхуки? Начните с генерации надёжного секрета, добавления проверки HMAC и настройки повторных попыток с идемпотентностью. Для быстрой проверки данных во время разработки попробуйте наш JSON Formatter, чтобы отформатировать и проверить JSON вебхука.