Skip to content

About

Приёмник вебхуков, который не пропускает чужое и не обрабатывает одно и то же дважды. HMAC-SHA256, идемпотентность, Python, FastAPI, 18 тестов.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

webhook-guard

Приёмник вебхуков, который не пропускает чужое и не обрабатывает одно и то же дважды.

Зачем

Две ошибки встречаются почти в каждой самодельной интеграции.

Первая — подпись либо не проверяется вовсе, либо сравнивается обычным ==, что даёт утечку по времени, либо считается по перекодированному телу, а не по сырому.

Вторая — нет дедупликации. Провайдер, не получивший 200 вовремя, повторит доставку: так устроены Telegram, WhatsApp Cloud API, платёжные шлюзы. Без защиты клиент получает два ответа на одно сообщение, а бухгалтерия — две записи об одной оплате.

Как работает

POST /webhook/{provider}
  X-Signature: sha256=<hmac>
  X-Event-Id: <id доставки>       # необязательно, но приоритетнее тела

Порядок проверок: подпись → идентификатор события → дедупликация → ответ. Сначала подпись — разбирать тело неподписанного запроса значит позволять чужому вводу управлять парсером.

Секрет задаётся на каждого провайдера отдельно: GUARD_SECRET_WHATSAPP, GUARD_SECRET_TELEGRAM. Один общий секрет означает, что утечка у одного провайдера открывает приём за всех.

GET /deliveries — журнал последних доставок, GET /health — живость и список настроенных провайдеров.

Решения, которые здесь главные

Отсутствие заголовка подписи — отказ, а не пропуск. Иначе достаточно убрать заголовок, чтобы обойти проверку целиком.

Подпись разбирается и как hex, и как base64, с префиксом и без. Провайдеры присылают её в разном виде и меняют формат без предупреждения.

Повтор получает 200 со статусом duplicate, а не 409. Для провайдера повтор должен выглядеть как успех, иначе он будет слать его снова по нарастающей.

Дедупликация — на уникальном ключе в базе, а не «сначала SELECT, потом INSERT». Два одновременных повтора при такой схеме проходят оба.

Идентификатор из заголовка важнее, чем из тела. Тело между повторами меняется (порядок ключей, добавленные поля), идентификатор доставки — нет.

Журнал чистится по TTL. Таблица дедупликации, которая растёт вечно, однажды станет причиной инцидента сама.

Запуск

pip install -r requirements.txt
export GUARD_SECRET_WHATSAPP=...
uvicorn guard.app:app --port 8080

Тесты

python -m pytest tests -q

18 тестов: подпись в трёх форматах, отказ без заголовка, побитовая чувствительность к телу, независимость провайдеров, атомарность дедупликации, чистка по TTL, извлечение идентификатора события.

About

Приёмник вебхуков, который не пропускает чужое и не обрабатывает одно и то же дважды. HMAC-SHA256, идемпотентность, Python, FastAPI, 18 тестов.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages