TAŇBA-OOH Разработчикам

Подписи вебхуков

Отправить запрос на ваш адрес HTTPS может кто угодно. Проверяйте подпись, прежде чем доверять доставке.

Что приходит

Каждая доставка это POST с телом JSON {"event", "occurred_at", "data"} и тремя заголовками:

ЗаголовокЗначение
X-Tanba-DeliveryНомер доставки. Он одинаков во всех повторах
X-Tanba-EventИмя события, например approval.issued
X-Tanba-Signaturet=<unix-секунды>,v1=<hex HMAC-SHA256>

События перечислены на странице вебхуков.

Секрет

Когда администратор организации создает вебхук в консоли, платформа показывает его секрет один раз. Он начинается с whsec_. Берите всю строку целиком как ключ. Храните секрет только на своем сервере.

Как проверить

  1. Прочитайте тело запроса как есть, до разбора JSON.
  2. Разделите заголовок на t и v1.
  3. Отклоните доставку, если t отличается от ваших часов больше чем на 5 минут.
  4. Вычислите HMAC-SHA256 от t, точки и тела запроса с секретом в качестве ключа. Запишите результат в hex.
  5. Сравните его с 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 и другие внутренние адреса отклоняются при создании вебхука и при каждой доставке, потому что имя разрешается в момент отправки.