Dmitriy Kononov.
Обсудим задачуСвязаться

Новости и разборы

Безопасность вебхуков: подпись, повторы и состояние операции

Как проверять источник вебхука, ограничивать повторную доставку и сохранять результат без дублей при сбоях интеграции.

БезопасностьОпубликовано:

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

Проверять сообщение до изменения данных

Для проверки подписи нужны исходные байты запроса и секрет конкретного подключения. Документация GitHub описывает проверку HMAC и безопасное сравнение результата. Нельзя сначала превратить JSON в объект, заново сериализовать его и считать, что последовательность байтов осталась прежней. Пробелы и порядок полей могут измениться.

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

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

Разделить повтор атаки и повтор доставки

Злоумышленник может заново отправить ранее полученное подписанное сообщение. Stripe включает время в подписываемые данные и описывает проверку допустимого возраста сообщения. Такая защита зависит от часов сервера и конкретного протокола. Нельзя без проверки переносить её на сервис, который не подписывает timestamp.

Есть и нормальные повторы: отправитель не получил подтверждение и доставляет событие снова. Рекомендации GitHub используют идентификатор доставки для распознавания повторного сообщения. Хранение таких идентификаторов полезно, но не заменяет защиту самого действия. Разные события могут относиться к одной бизнес-операции, поэтому у неё нужен собственный стабильный ключ.

Принять событие и выполнить действие

Практическая архитектура может сначала проверить запрос и надёжно сохранить принятую задачу, а затем передать её рабочему процессу. Подтверждение приёма следует выдавать после сохранения, иначе сбой между ответом и записью может потерять событие. Наличие очереди не снимает необходимость проверять ошибки записи и наблюдать за отставанием обработки.

Для операции полезны состояния «принято», «выполняется», «завершено» и «требует проверки». Переходы должны учитывать повторный запуск и одновременную обработку. Простая последовательность «посмотреть, существует ли запись, затем создать» может пропустить два параллельных запроса. Нужны атомарное ограничение уникальности и согласованное обновление состояния.

Самая трудная граница возникает при внешнем действии: API уже выполнил его, а ответ потерялся. Пометка события обработанным заранее создаёт риск потери действия, пометка после ответа создаёт риск повторения. Если внешняя система поддерживает idempotency key, следует использовать стабильный ключ операции. Если такой возможности нет, потребуется сверка и состояние неопределённого результата.

Что проверить до запуска

Набор проверок должен включать изменённое тело, неверную подпись, повтор одного события и одновременную доставку. Отдельно проверяются сбой очереди, перезапуск рабочего процесса и потеря ответа внешнего API. Эти сценарии показывают больше, чем успешная передача одного тестового сообщения.

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

При проектировании API-интеграции стоит согласовать поведение при неопределённом результате. Это позволяет заранее определить, где процесс продолжится автоматически, а где потребуется человек.

Источники проверены 7 октября 2026 года. Протоколы разных провайдеров отличаются; схема состояний и проверки являются рекомендациями автора для адаптации к конкретной интеграции.

Практический контекст для такого разбора даёт Dent-Picks: я реализовал создание лида в GoHighLevel после входящего звонка на IVR и другие синхронизации. В этом примере результатом является запись, с которой команда продолжает работу. Он помогает сформулировать вопросы о повторном событии и источнике данных. Публичный кейс не раскрывает транспорт сообщений или способ проверки подписи; требования этой статьи нельзя считать описанием внедрённых там механизмов.

В моём разборе связи документов, CRM и задач отдельно обсуждаются устойчивый идентификатор и повтор после таймаута. Прочитайте его вместе с требованиями к подписи: подтверждение отправителя и сохранение одного рабочего результата требуют разных проверок.

Источники