Проблема: доверять запросу

Когда сервер получает запрос API, откуда он знает, что вызывающий — тот, за кого себя выдаёт, и что никто не изменил запрос при передаче? Отправлять общий секрет с каждым запросом работало бы, пока этот секрет не попадёт в журнал, кеш или не утечёт. Подпись запросов решает это, ни разу не передавая секрет: вызывающий доказывает, что владеет секретом, используя его для подписи запроса, а сервер проверяет подпись.

Как работает подпись

Обе стороны разделяют секретный ключ. Чтобы подписать запрос, клиент:

  1. Строит каноническую строку из тех частей запроса, которые не должны меняться, обычно метод HTTP, путь, ключевые заголовки, метку времени и хеш тела. «Каноническая» означает, что обе стороны собирают её ровно одним и тем же согласованным способом, чтобы считать над идентичным входом.
  2. Вычисляет HMAC-SHA256(каноническая строка, секрет) и прикрепляет к запросу, обычно в заголовке вроде Authorization или X-Signature.

Сервер, владея тем же секретом, заново строит каноническую строку из полученного запроса и сам вычисляет HMAC. Если его значение совпадает с тем, что в заголовке, разом доказываются две вещи: вызывающий владеет секретом (подлинность), и ничто, покрытое подписью, не было изменено (целостность). Сам секрет никогда не путешествует.

Реальный пример: AWS Signature Version 4

SigV4 от Amazon — самый известный пример. Каждый запрос подписывается с HMAC-SHA256 над каноническим запросом, с использованием ключа подписи, выведенного из секретного ключа доступа, даты, региона и сервиса. Сервер заново выводит ключ и пересчитывает подпись; несовпадение отклоняется. Тот же шаблон, канонизировать, HMAC, сравнить, лежит в основе бесчисленных других схем аутентификации API.

Вебхуки: та же идея, наоборот

Вебхуки переворачивают направление. Провайдер (платёжный процессор, Git-хост) вызывает ваш эндпоинт, чтобы уведомить вас о событии, и подписывает полезную нагрузку секретом, который вы оба разделяете, отправляя HMAC в заголовке. Ваш эндпоинт пересчитывает HMAC над полученным телом и сравнивает, и так вы отклоняете поддельные вызовы от всякого, кто не владеет секретом. Всегда сравнивайте проверкой с постоянным временем, чтобы не утекала информация через тайминг.

Защита от повтора

Корректный подписанный запрос, перехваченный атакующим, можно повторить дословно. Стандартные защиты — включать метку времени в подписанные данные и отклонять запросы вне короткого окна, и опционально nonce (одноразовое значение, которое сервер ненадолго запоминает), чтобы один и тот же запрос нельзя было принять дважды. Подпись доказывает подлинность; метка времени и nonce не дают переиспользовать старый, подлинный запрос.

Инструмент HMAC вычисляет HMAC-SHA256 (и -384/512), лежащий в сердце каждой из этих схем, над сообщением и ключом, которые вы предоставляете, целиком в вашем браузере.