Как защитить конечные точки Webhook с помощью HMAC

Webhooks повсеместно используются в SaaS-интеграциях, и на это есть веская причина. Они представляют собой простой и быстрый способ передачи данных через HTTP-обратные вызовы между системами на основе изменений данных в этих системах. Моя компания помогает SaaS-командам создавать встроенные интеграции с другими приложениями своих клиентов, и многие из наших клиентов используют веб-крючки для своих интеграций.

За последние несколько недель мы помогли нескольким нашим клиентам, которым нужно было обеспечить безопасность конечных точек webhooks. В этом посте мы опишем подход, который мы рекомендуем. Но сначала давайте заложим некоторую основу.

Как работают веб-крючки?

Вкратце, у приложения-источника есть веб-крючок, а у приложения-получателя — конечная точка веб-крючка; на основе некоторого события, происходящего в приложении-источнике, веб-крючок отправляет HTTP-запрос на конечную точку веб-крючка.

Вот простой пример тела HTTP-запроса (или полезной нагрузки):

{
  "event": "WAREHOUSE_UPDATE",
  "updates": [
    {
      "item": "gadgets",
      "action": "add",
      "quantity": 20
    },
    {
      "item": "widgets",
      "action": "remove",
      "quantity": 10
    }
  ]
}
Войти в полноэкранный режим Выйти из полноэкранного режима

Но как убедиться, что целевое приложение получает действительные данные от приложения-источника, а не фиктивные данные от злоумышленника, который подделал веб-крючок?

Короткий ответ заключается в том, что вам нужно настроить webhook так, чтобы конечная точка получала HTTP-запрос и уникальный ключ, который конечная точка может использовать для проверки данных. Но прежде чем перейти к деталям, давайте вкратце расскажем о хэшировании.

Что такое хэширование?

В самом простом смысле хеширование — это процесс преобразования значения (или ключа) в другое значение. Даже если вы не работали с хэшированием раньше, вы наверняка знаете о MD5, SHA-256 или RipeMD-128. Каждый из них — это название алгоритма хэширования (он же криптографическая хэш-функция).

Давайте посмотрим, что каждый алгоритм делает с классической строкой:

  • MD5 хэширует Hello World! до ed076287532e86365e841e92bfc50d8c.
  • SHA-256 хэш Hello World! до 7f83b1657ff1fc53b92dc18148a1d65dfc2d4b1fa3d677284addd200126d9069
  • RipeMD-128 хэширует Hello World! до 24e23e5c25bc06c8aa43b696c1e11669.

Важным моментом является то, что алгоритм хэширует значение каждый раз одним и тем же способом. Если мы не меняем нашу строку («Hello World!»), то и результирующее хэш-значение не меняется.

Однако если что-то в строке изменится, то изменится и хэш. Например, давайте запишем ‘H’ в нижнем регистре, чтобы получилось ‘Hello World!’, и посмотрим, что это даст:

  • MD5 хэширует hello World! до 41d0c351efedf7fdb9a5dc8a1ed4b4e3.
  • SHA-256 хэш hello World! до e4ad0102dc2523443333d808b91a989b71c2439d7362aca6538d49f76baaa5ca
  • RipeMD-128 хэширует hello World! до b5cf338f17d6796ba0312e0d78c70831.

Небольшое изменение, но разница в результате очевидна.

Хотя хэширование не позволяет нам полностью решить нашу первоначальную проблему (кто-то посылает фальшивые данные на конечную точку webhook), оно приводит нас прямо к HMAC.

Что такое HMAC?

HMAC, или хэшированный код аутентификации сообщений, — это метод аутентификации, который использует не один, а два ключа. Первый ключ — это тело HTTP-запроса, а второй — секретный криптографический ключ. Когда вы реализуете HMAC для своего webhook, вы будете использовать оба этих ключа плюс алгоритм, такой как MD5, SHA-256 или RipeMD-128, чтобы убедиться, что HTTP-запрос, который появляется на конечной точке вашего webhook, легитимен.

Как работает HMAC?

Прежде чем приложение-источник отправит HTTP-запрос через webhook, оно хэширует полезную нагрузку (тело запроса) с помощью HMAC, используя секретный ключ. Полученный хэш затем включается в HTTP-запрос в качестве заголовка, и весь запрос (заголовок и тело) отправляется на конечную точку webhook.

Получив HTTP-запрос, целевое приложение хэширует тело запроса секретным ключом, а затем сравнивает результат с хэшем, указанным в заголовке. Если значения совпадают, целевое приложение понимает, что данные легальны, и обрабатывает их. Если значения не совпадают, приложение-получатель отклоняет данные и выполняет любой код, написанный для этого сценария — возможно, создает запись в журнале или отправляет уведомление.

Если кто-то попытается подделать полезную нагрузку, он не сможет сгенерировать правильный хэш, поскольку у него нет секретного ключа. Дверь закрыта.

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

Но, скажете вы, разве не может кто-нибудь перехватить HTTP-запрос и пересмотреть хэш в заголовке, чтобы узнать секрет? Короткий ответ: нет. Хеширование — это односторонняя функция. Чтобы взломать хэш с достаточно сложным секретом, нам потребуется больше вычислительной мощности и времени, чем есть у каждого из нас.

Приложения, использующие HMAC для конечных точек webhook

Некоторые известные приложения в настоящее время используют HMAC для защиты своих конечных точек webhook:

  • Slack: Предоставляет секрет подписи при создании приложения Slack. Когда он отправляет полезную нагрузку webhook, он хэширует полезную нагрузку и метку времени webhook этим секретом с помощью SHA256. Запрос webhook включает полученный хэш в качестве заголовка X-Slack-Signature.
  • Dropbox: Генерирует App Secret при создании приложения Dropbox и использует этот секрет для генерации HMAC-хэшей webhook и аутентификации пользователей по OAuth 2.0. Он хэширует полезную нагрузку webhook с помощью SHA256 и отправляет хэш в виде заголовка X-Dropbox-Signature.
  • Shopify: Создает секретный ключ API и хэширует полезную нагрузку с помощью этого ключа и SHA256. Он отправляет хэш в виде заголовка X-Shopify-Hmac-SHA256.

HMAC имеет широкую языковую поддержку

Вы можете использовать практически любой современный язык для вычисления хэшей HMAC. Вот несколько ссылок на популярные языки с возможностями HMAC:

  • NodeJS
  • Python
  • PHP
  • .NET C#

Пример кода для HMAC

Наконец, что бы все это было без кода? Вот пример того, как это можно настроить в NodeJS, используя встроенный криптомодуль:

const crypto = require("crypto");

const SECRET_KEY = "secret-FA782CF7-060E-484E-B3DC-055CF2C9ED99";

const payload = JSON.stringify({
  event: "REFUND_REQUEST",
  user: "realcustomer@notabaddie.com",
  amount: "50.25",
});

const hash = crypto
  .createHmac("sha256", SECRET_KEY)
  .update(payload, "utf-8")
  .digest("hex");

console.log(hash); // Prints d12f95e3f98240cff00b2743160455fdf70cb8d431db2981a9af8414fc4ad5f8
Вход в полноэкранный режим Выход из полноэкранного режима

Соответствующий HTTP-запрос с использованием HMAC может выглядеть следующим образом:

curl https://my.webhook.endpoint.com/callback 
  --request POST 
  --header "x-hmac-hash: d12f95e3f98240cff00b2743160455fdf70cb8d431db2981a9af8414fc4ad5f8" 
  --data '{"event":"REFUND_REQUEST","user":"realcustomer@notabaddie.com","amount":"50.25"}'
Вход в полноэкранный режим Выход из полноэкранного режима

Даже если злоумышленник перехватит ваш HTTP-запрос, он не сможет отправить запрос на возврат миллиона долларов на свой собственный адрес электронной почты, поскольку не сможет правильно подписать запрос без секретного ключа.

Заключение

Использование HMAC не требует от вас изучения нового языка или глубокого понимания шифрования, но оно позволяет защитить целостность данных, которые вы передаете через webhooks.

Оцените статью
Procodings.ru
Добавить комментарий