Вебхуки: подписанные данные и повторные попытки
Вы только что интегрировали сторонний сервис, который отправляет вебхуки на ваш эндпоинт. В тестах всё работает, но в продакшене вы замечаете пропущенные события, дублирующиеся доставки и иногда возникают вопросы безопасности. Вебхуки просты в концепции, но сложны на практике — особенно когда дело доходит до проверки подлинности и обработки сбоев.
В этом руководстве объясняется, как защитить вебхуки с помощью подписанных данных и реализовать надежные механизмы повторных попыток, чтобы вы могли создавать надежные интеграции.
Что такое вебхуки?
Вебхуки — это определяемые пользователем HTTP-колбэки. Когда в исходной системе происходит событие (например, завершается платеж), она отправляет HTTP POST-запрос на указанный вами URL, содержащий данные о событии. Это позволяет организовать связь между сервисами в реальном времени без опроса.
Но вебхуки создают проблемы: как узнать, что запрос действительно пришел от ожидаемого отправителя? Что, если ваш эндпоинт временно недоступен? Здесь на помощь приходят подписанные данные и повторные попытки.
Защита вебхуков с помощью подписанных данных
Подписанные данные используют криптографическую подпись, чтобы доказать, что запрос исходит от отправителя и не был изменен. Распространенный подход — HMAC (код аутентификации сообщения на основе хэша) с общим секретом.
Как работают подписи HMAC
Отправитель вычисляет HMAC для тела запроса (а иногда и для заголовков, таких как временная метка) с использованием секретного ключа. Подпись отправляется в заголовке (например, X-Signature). Ваш сервер пересчитывает HMAC и сравнивает его с полученной подписью. Если они совпадают, запрос подлинный.
Вот минимальный пример на Python с использованием Flask:
import hmac
import hashlib
from flask import Flask, request, abort
app = Flask(__name__)
SECRET = b'your_shared_secret'
@app.route('/webhook', methods=['POST'])
def webhook():
signature = request.headers.get('X-Signature')
if not signature:
abort(400, 'Missing signature')
body = request.get_data()
expected = hmac.new(SECRET, body, hashlib.sha256).hexdigest()
if not hmac.compare_digest(expected, signature):
abort(401, 'Invalid signature')
# Process the event
return '', 200
Ключевые моменты:
- Всегда используйте функцию сравнения с постоянным временем (например,
hmac.compare_digest), чтобы предотвратить атаки по времени. - Включайте временную метку в подписываемые данные для предотвращения атак повторного воспроизведения; отклоняйте запросы старше нескольких минут.
- Храните секрет в безопасности — никогда не раскрывайте его на клиентской стороне.
Дополнительные меры безопасности
- HTTPS: Всегда используйте HTTPS для защиты данных при передаче.
- Белый список IP: Если провайдер публикует диапазоны IP, ограничьте запросы этими IP.
- Идемпотентность: Используйте идентификаторы событий для дедупликации, так как повторные попытки могут вызывать дубликаты.
Реализация надежных повторных попыток
Потребители вебхуков могут давать сбои: ваш сервер может быть недоступен или обработка может завершиться по таймауту. Отправители должны повторять неудачные доставки с экспоненциальной задержкой. Как потребитель, вы должны корректно обрабатывать повторные попытки.
На стороне отправителя (если вы создаете систему вебхуков)
- Быстро возвращайте 2xx: Подтверждайте получение до обработки, чтобы избежать таймаутов. Используйте очередь для асинхронной работы.
- Повторяйте при сбое: Если 2xx не получен в течение таймаута (например, 5 секунд), повторите с увеличивающимися задержками (например, 1 с, 5 с, 30 с, 5 мин, 30 мин).
- Ограничьте количество попыток: После максимального числа попыток (например, 5) пометьте доставку как неудачную и отправьте оповещение.
- Включите уникальный идентификатор события: Чтобы потребители могли выполнять дедупликацию.
На стороне потребителя (ваш эндпоинт)
- Отвечайте быстро: Немедленно возвращайте 2xx и при необходимости обрабатывайте асинхронно.
- Будьте идемпотентны: Используйте идентификатор события, чтобы избежать повторной обработки одного и того же события.
- Обрабатывайте дубликаты: Сохраняйте идентификаторы обработанных событий в течение разумного периода.
- Регистрируйте сбои: Если обработка не удалась, верните 5xx, чтобы инициировать повторную попытку, но убедитесь, что вы не застряли в цикле.
Сравнение: подписанные данные vs. базовая аутентификация
| Подход | Безопасность | Сложность | Защита от повторного воспроизведения |
|---|---|---|---|
| Подпись HMAC | Высокая (целостность + подлинность) | Средняя | Да (с временной меткой) |
| Базовая аутентификация | Низкая (учетные данные в заголовке) | Низкая | Нет |
| Белый список IP | Средняя (на уровне сети) | Низкая | Нет |
Тестирование вебхуков локально
Во время разработки вам нужен публичный URL для получения вебхуков. Инструменты вроде ngrok или localtunnel могут предоставить доступ к вашему локальному серверу. Всегда тестируйте проверку подписи и поведение при повторных попытках с имитацией сбоев.
При анализе полезной нагрузки вебхуков вам может потребоваться форматирование JSON для удобства чтения. JSON Formatter поможет вам красиво отформатировать и проверить тело JSON.
Часто задаваемые вопросы
Что такое подписанная полезная нагрузка вебхука?
Подписанная полезная нагрузка включает криптографическую подпись (часто HMAC), которая позволяет получателю проверить подлинность и целостность запроса.
Сколько раз следует повторять вебхук?
Фиксированного числа нет; обычно практикуется от 3 до 10 повторных попыток с экспоненциальной задержкой. Ключевым является баланс между надежностью и использованием ресурсов, а также оповещение о постоянных сбоях.
Можно ли использовать вебхуки без подписей?
Это не рекомендуется. Без подписей вы не можете проверить отправителя, что делает ваш эндпоинт уязвимым для подмены. Всегда используйте подписи в продакшене.
Готовы отлаживать полезные нагрузки вебхуков? Попробуйте наш JSON Formatter, чтобы быстро проверить и отформатировать данные JSON.