Подпись webhook SMS: как проверять подлинность DLR

1 мин
Время чтения

Webhook со статусом доставки меняет данные и может запускать бизнес-логику. Если endpoint принимает любой POST без проверки, посторонний отправитель способен подделать delivered, создать поток ошибок или перегрузить систему. HTTPS защищает канал передачи, но не доказывает, что запрос сформировал именно SMS-провайдер.

Подпись webhook связывает тело запроса с секретом, известным двум сторонам. Система вычисляет контрольное значение и сравнивает его с заголовком провайдера до разбора и изменения статуса.

Следовать контракту провайдера

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

Часто используется HMAC-SHA256 по сырому телу и timestamp, но возможны другие варианты. Сохраните официальный пример запроса как тестовый fixture и проверяйте его при обновлении SDK или фреймворка.

Сырое тело запроса

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

Сначала проверяются лимит размера, обязательные заголовки и подпись. Только после успешной аутентификации JSON разбирается и проходит валидацию схемы.

Constant-time comparison

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

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

Timestamp и replay

Корректно подписанный запрос можно перехватить и повторить позднее. Добавление timestamp в подписываемую строку и проверка допустимого окна ограничивают replay. Часы серверов должны быть синхронизированы, а допустимое отклонение — документировано.

Одного времени недостаточно: внутри окна событие может прийти повторно законно. Обработчик остаётся идемпотентным и дедуплицирует webhook по event ID либо устойчивой комбинации message ID, статуса и времени события.

Ротация секрета

Во время ротации endpoint может короткое время принимать подписи от текущего и следующего секрета. Каждый имеет внутренний key ID и срок действия. После перехода старый секрет удаляется, а попытки его использования становятся сигналом.

Не храните webhook secret рядом с публичным URL или в исходном коде. Процесс управления такой же, как для ротации API-ключей SMS, но права входящего и исходящего канала разделяются.

IP allowlist

Список IP может быть дополнительным слоем, если провайдер публикует стабильные диапазоны. Он не заменяет подпись: адреса меняются, запрос может идти через прокси, а доверенная сеть тоже не гарантирует целостность тела.

Обновление allowlist автоматизируют и тестируют. В противном случае легитимные DLR внезапно перестанут приходить после изменения инфраструктуры провайдера.

Обработка ошибок

При неверной подписи endpoint возвращает отказ без подробного объяснения. В лог попадают время, источник, безопасный request ID и причина категории, но не секрет, полная подпись или чувствительный payload.

Алерт на каждую единичную ошибку создаёт шум: сканеры интернета регулярно находят публичные URL. Сигнал строят по устойчивому росту, совпадению с известным IP провайдера или появлению после ротации.

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

После проверки подписи и минимальной схемы webhook лучше надёжно поставить во внутреннюю очередь и быстро вернуть ожидаемый 2xx. Долгая бизнес-обработка увеличивает число повторов со стороны провайдера.

Сам быстрый ответ не означает, что событие потеряно. Сначала оно сохраняется атомарно, затем обрабатывается асинхронно. Общий pipeline описан в статье про webhook статусов SMS.

Тесты

Обязательные сценарии:

  • корректное тело и подпись;
  • изменение одного байта payload;
  • неверный секрет;
  • отсутствующий заголовок;
  • старый и будущий timestamp;
  • повтор корректного события;
  • два активных секрета во время ротации;
  • JSON с другим порядком полей;
  • слишком большое тело;
  • запрос после отзыва старого секрета.

Тест должен использовать сырые байты официального примера, а не объект, который framework сериализует заново.

Чек-лист

  • алгоритм реализован точно по документации провайдера;
  • подпись считается по сырым байтам;
  • сравнение выполняется constant-time функцией;
  • timestamp входит в проверку, если это предусмотрено контрактом;
  • повторы дедуплицируются;
  • секрет хранится и ротируется отдельно от API-ключа;
  • IP allowlist остаётся дополнительным слоем;
  • до аутентификации не меняются бизнес-данные;
  • ошибки не раскрывают детали проверки.

Итог

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

Обновлено 6 октября, 2026

Общая учётная запись администратора удобна до...

7 октября, 2026

Интеграция может месяцами работать, а затем...

Подходит каждому
SMS-рассылка для любой сферы бизнеса
Узнайте как внедрить SMS-рассылку в ваш бизнес вместе с quicktel
Оставьте номер и вам перезвонит менеджер.