Соберите тело события, укажите секрет и получите готовый заголовок подписи. Всё считается в браузере — секрет никуда не уходит.
Секунды Unix. Входит в подписываемую строку и защищает от повторной отправки перехваченного запроса.
Подписываемая строка
1787724011.<тело>Введите секрет, чтобы получить подписьПодпись считается по алгоритму HMAC-SHA256 прямо в браузере через Web Crypto. Секрет и тело события никуда не отправляются. На стороне получателя подпись сверяют по сырому телу запроса, а не по заново собранному JSON: повторная сборка меняет пробелы и порядок ключей, и подпись перестаёт сходиться.
Адрес вебхука открыт всему интернету: его знает отправитель, но постучаться туда может кто угодно. Подпись решает эту задачу. Отправитель считает от тела запроса код HMAC на общем секрете, а получатель повторяет расчёт у себя и сверяет результат.
Вместе с телом подписывают метку времени. Без неё перехваченный запрос можно воспроизвести когда угодно, и подпись останется верной. С меткой получатель отвергнет запрос, который пришёл слишком поздно.
Считают подпись по разобранному JSON. Тело читают, превращают в объект, потом собирают обратно в строку — и подпись перестаёт сходиться, потому что изменились пробелы и порядок ключей. Проверять нужно сырое тело запроса.
Сравнивают строки обычным способом. Такое сравнение обрывается на первом несовпавшем символе, и по времени ответа можно подобрать подпись посимвольно. Нужна функция сравнения с постоянным временем выполнения.
Используют один секрет на всех. Тогда любой получатель сможет подделать событие для любого другого. У каждого эндпоинта должен быть свой секрет.
Обработчик обязан ответить быстро: типичный таймаут вебхука — от трёх до пяти секунд, дальше отправитель считает доставку неудачной и начинает повторять. Поэтому проверяют подпись, кладут задачу в очередь и сразу отвечают 200, а всю настоящую работу делают за пределами HTTP-запроса.
Обе стороны этой механики мы разобрали подробно: приём чужих событий — в статье про настройку API на сайте, отдачу своих — в статье про вебхуки для партнёров.
Всё о подписи вебхуков
Без неё перехваченный запрос можно повторить хоть через месяц, и подпись останется верной. Метка времени входит в подписываемую строку, поэтому получатель может отвергнуть запрос, если он слишком старый. Обычно допускают расхождение в несколько минут.
Самая частая причина — подпись считают по заново собранному JSON вместо сырого тела запроса. Разбор и повторная сборка меняют пробелы и порядок ключей, а значит, и подпись. Проверять нужно ровно ту строку байтов, которая пришла по сети.
Нет. Подпись считается в браузере через Web Crypto API. Ни секрет, ни тело события не уходят на сервер и никуда не записываются.
Инструмент собирает заголовок в распространённом виде «t=метка,v1=подпись». Префикс версии нужен, чтобы однажды сменить алгоритм, не ломая уже подключённых получателей: какое-то время вы шлёте v1 и v2 одновременно, а потом убираете старую версию.
Функцией сравнения с постоянным временем выполнения, например timingSafeEqual в Node.js. Обычное сравнение строк завершается на первом несовпавшем символе и по времени ответа подсказывает, сколько символов угадано верно.
Подпись подтверждает отправителя и целостность тела, но не защищает от дублей. Отправители гарантируют доставку хотя бы один раз, поэтому обработчик обязан переживать повторы: сохраняйте идентификатор события и пропускайте уже обработанные.
Спроектируем доставку вебхуков с подписью, очередью и повторами — и возьмём её на поддержку.
Приём вебхуков, проверка подписи, таймауты и идемпотентность — разбор ошибок, которые видно только в проде
Подпись, повторы, очередь и мёртвые эндпоинты — как спроектировать доставку событий партнёрам
Спроектируем обмен событиями между вашими сервисами и возьмём его на поддержку