Подписи вебхуков
Отправить запрос на ваш адрес HTTPS может кто угодно. Проверяйте подпись, прежде чем доверять доставке.
Что приходит
Каждая доставка это POST с телом JSON {"event", "occurred_at", "data"} и тремя заголовками:
| Заголовок | Значение |
|---|---|
X-Tanba-Delivery | Номер доставки. Он одинаков во всех повторах |
X-Tanba-Event | Имя события, например approval.issued |
X-Tanba-Signature | t=<unix-секунды>,v1=<hex HMAC-SHA256> |
События перечислены на странице вебхуков.
Секрет
Когда администратор организации создает вебхук в консоли, платформа показывает его секрет один раз. Он начинается с whsec_. Берите всю строку целиком как ключ. Храните секрет только на своем сервере.
Как проверить
- Прочитайте тело запроса как есть, до разбора JSON.
- Разделите заголовок на
tиv1. - Отклоните доставку, если
tотличается от ваших часов больше чем на 5 минут. - Вычислите HMAC-SHA256 от
t, точки и тела запроса с секретом в качестве ключа. Запишите результат в hex. - Сравните его с
v1за постоянное время.
Python:
import hmac, hashlib, time
def verify(secret: str, header: str, body: bytes, tolerance=300) -> bool:
parts = dict(p.split("=", 1) for p in header.split(","))
t, sig = parts["t"], parts["v1"]
if abs(time.time() - int(t)) > tolerance:
return False
mac = hmac.new(secret.encode(), t.encode() + b"." + body, hashlib.sha256).hexdigest()
return hmac.compare_digest(mac, sig)
Go:
func verify(secret string, header string, body []byte, now time.Time) bool {
var t, sig string
for _, part := range strings.Split(header, ",") {
k, v, _ := strings.Cut(part, "=")
switch k {
case "t":
t = v
case "v1":
sig = v
}
}
secs, err := strconv.ParseInt(t, 10, 64)
if err != nil || now.Sub(time.Unix(secs, 0)).Abs() > 5*time.Minute {
return false
}
mac := hmac.New(sha256.New, []byte(secret))
mac.Write([]byte(t + "."))
mac.Write(body)
want, err := hex.DecodeString(sig)
return err == nil && hmac.Equal(mac.Sum(nil), want)
}
Повторы и дубли
Отвечайте любым статусом 2xx в течение 10 секунд. Любой другой ответ приводит к повтору: первый повтор через 1 минуту, затем пауза удваивается до 6 часов, всего не больше 12 попыток. После этого доставка получает статус failed.
Одна доставка может прийти больше одного раза. Сохраняйте X-Tanba-Delivery и пропускайте уже обработанные доставки. Долгую работу делайте после того, как ответили.
Адреса, которые отклоняются
Адреса loopback, частные, link-local и другие внутренние адреса отклоняются при создании вебхука и при каждой доставке, потому что имя разрешается в момент отправки.