Skip to main content
Каждый запрос, который мы отправляем на ваш сервер — доставка webhook и вызовы конечных точек инструментов — содержит подпись HMAC-SHA256 в заголовке X-ThunderPhone-Signature. Один раз реализуйте проверку правильно и подключите этот же вспомогательный метод к каждому обработчику.

Алгоритм

  1. Считайте исходное тело запроса — точные байты, которые мы отправили вам через POST.
  2. Вычислите hmac_sha256(secret, body).hexdigest().
  3. Сравните за постоянное время со значением X-ThunderPhone-Signature. (Наивное сравнение строк раскрывает информацию о времени выполнения.)
Мы подписываем ровно те байты, которые передаём, поэтому проверка исходного тела всегда работает. Эти байты также являются канонической сериализацией JSON полезной нагрузки — ключи отсортированы по алфавиту, компактные разделители (, и : без пробелов), UTF-8. Это даёт вам второй, полностью эквивалентный способ, если ваш фреймворк предоставляет только разобранный JSON: повторно сериализуйте данные канонически и вычислите для них HMAC.
Предпочтительно использовать исходное тело — это на один шаг меньше и позволяет избежать особенностей повторного преобразования чисел JSON в некоторых языках.

Какой секрет?

Храните секрет в менеджере секретов или переменной окружения — никогда не добавляйте его в коммит.

Эталонные реализации

Все четыре варианта проверяют исходное тело запроса:

Интеграция для конкретных фреймворков

Проверка вызовов инструментов

Когда агент напрямую вызывает один из ваших функциональных инструментов (у инструмента есть endpoint), запрос содержит два заголовка ThunderPhone вместе с настроенными вами endpoint.headers:
  • X-ThunderPhone-Call-ID — числовой идентификатор текущего звонка.
  • X-ThunderPhone-Signature — HMAC-SHA256 с ключом в виде вашего секрета вебхука на уровне организации, вычисленный по точным байтам тела запроса.
Тот же помощник verify() работает без изменений, с двумя нюансами:
  1. У инструментов GET / DELETE нет тела. Аргументы передаются как параметры запроса, а подпись вычисляется по пустой строке байтов — то есть verify(b"", sig, secret) (Python) или verify(Buffer.alloc(0), sig, secret) (Node). Не хешируйте строку запроса.
  2. У организаций без настроенного устаревшего вебхука нет секрета организации. В этом случае вызовы инструментов содержат только X-ThunderPhone-Call-ID и не содержат заголовка подписи. Настройте устаревший вебхук (PUT /v1/webhook), чтобы получить секрет подписи, или аутентифицируйте вызовы инструментов с помощью собственного заголовка через endpoint.headers.
Диспетчеризация инструментов в режиме вебхука (инструменты без endpoint, доставляемые в вебхук вашей организации как telephony.tool / web.tool) — это обычный подписанный вебхук, поэтому применяется стандартный рецепт выше. Оба формата запросов описаны в разделе Функциональные инструменты.

Распространённые ошибки

Разбор тела и его повторная сериализация с настройками по умолчанию вашей JSON-библиотеки (пробелы после , / :, ключи в порядке вставки) создают другие байты и нарушают HMAC. Проверяйте исходное тело — или, если необходимо повторно сериализовать его, точно соблюдайте нашу каноническую форму: отсортированные ключи, компактные разделители, UTF-8.
Промежуточное ПО Express express.json() считывает поток тела, и вы теряете исходные байты. Используйте express.raw() специально для маршрута вебхука или буферизуйте исходное тело в предварительном промежуточном ПО. То же относится к NestJS / Koa — ознакомьтесь с их документацией по «исходному телу».
expected === signature в JS или expected == signature в Python — это сравнения с переменным временем выполнения. Используйте crypto.timingSafeEqual или hmac.compare_digest соответственно. Разница в производительности отсутствует.
Прямые вызовы эндпоинтов инструментов подписываются с помощью секрета вебхука на уровне организации (GET /v1/webhook) — а не каким-либо секретом для конкретного эндпоинта из /v1/developer/webhook-endpoints. Повторно используйте ту же функцию verify(), но убедитесь, что передаёте ей секрет организации для маршрутов инструментов.
Для методов инструментов без тела подпись охватывает пустую строку байтов, сохраняя единый универсальный подход: вычисляйте HMAC от исходного тела запроса, каким бы оно ни было. Хеш URL или строки запроса никогда не совпадёт.
Возврат 200 при неудачной проверке делает обработчик целью для повторной атаки. Всегда отвечайте статусом не из диапазона 2xx, если проверка не пройдена.

Следующие шаги

Обзор вебхуков

Семантика доставки, повторы, исходные IP-адреса.

Эндпоинты вебхуков

Управляйте несколькими URL, ротируйте секреты.

Инструменты-функции

Два пути вызова инструментов и формы их запросов.

Интеграции инструментов

Создайте полную интеграцию на основе инструментов от начала до конца.