[JS] Руководство для начинающих: защита конечной точки веб-перехватчика приложения GitHub

Учебник для начинающих по веб-перехватчикам, подписям HMAC и промежуточному программному обеспечению. Узнайте, как защитить свой сервер, написав промежуточное ПО Express, которое проверяет подлинность полезных данных событий, отправляемых GitHub.


Приложения GitHub позволяют настроить веб-перехватчик для прослушивания всевозможных событий в организациях и репозиториях. Они могут варьироваться от новых или обновленных выпусков до PR-комментариев и развертываний. С использованием API-интерфейс GitHub ваше приложение становится способным реагировать на эти события. Возможности действительно безграничны для тех, кто любит автоматизацию.

Но с большой силой приходит большая ответственность! Вы не хотели бы автоматизировать действия из ненадежных или вредоносных полезных нагрузок.

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

Что такое вебхук?

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

Скажем, у нас есть два сервера — сервер событий и прослушивающий сервер. В контексте GitHub Apps сервер событий — это GitHub, а прослушивающий сервер — это сервер вашего приложения.

Серверы прослушивания могут регистрироваться на серверах событий, чтобы крюк в происходящие события. Они настраивают, о каких событиях они хотят получать уведомления и на какую конечную точку они хотят, чтобы уведомления (запросы) отправлялись. Эта конечная точка называется конечной точкой веб-перехватчика прослушивающего сервера.

Распространенным способом защиты конечных точек веб-перехватчика является использование секрета веб-перехватчика. Этот секрет в сочетании с некоторой криптографией обеспечивает механизм доверия между серверами событий и серверами прослушивания.

Как работает секрет вебхука

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

Эта подпись прикрепляется в качестве заголовка к запросам событий, отправляемым на сервер вашего приложения. GitHub, в частности, использует HMAC-SHA1, но алгоритм хеширования не зависит от того, как работают HMAC.

Подпись HMAC не шифрует полезные данные. Это проверяемый хэш подписи сделано из секрета [our webhook secret] и текстовые данные [GitHub event payload]. Вы также можете зашифровать данные, но это необязательный шаг.

Подпись используется для проверки легитимности как источника, так и самих данных. Когда запрос получен прослушивающим сервером, он использует свой сохраненный секрет и полезную нагрузку события для создания своей собственной подписи HMAC-SHA1 для сравнения с подписью заголовка.

Когда секрет и полезная нагрузка одинаковы с обеих сторон, подписи HMAC будут совпадать. Это совпадение подтверждает подлинность запроса и данных.

Как насчет подделки данных?

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

Но что делать по проводу, если сообщение перехвачено и изменено? Как здесь нам поможет подпись HMAC?

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

Злоумышленник будет иметь только подпись из заголовка и исходную полезную нагрузку. У них не было бы [reasonable] способ деконструировать нашу тайну из двух —пока вы используете сильный секрет.

Без секрета у них не было бы возможности сфальсифицировать новую подпись для сопряжения с измененными данными. Любая попытка фальсификации полезной нагрузки приведет к созданию другой сгенерированной подписи, которая не сможет сравниться с подписью заголовка.

Люди чертовски умны. На сборку!

Настраивать

Перейдите на страницу своего приложения GitHub и прокрутите вниз до раздела веб-перехватчиков.

Вы можете найти страницу своего приложения в разделе [user or org] настройки → Приложения GitHub

URL-адрес вебхука

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

  • npm i ngrok --save-dev
  • запустите сервер приложений на выбранном вами порту
  • ngrok http <Your App's Local Port>
  • затем скопируйте https ссылка, которую он генерирует

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

Обязательно выключите ngrok когда вы закончите его использовать. Не оставляйте открытый порт, даже если он открыт через случайно сгенерированный URL-адрес.

Тсссс

Далее сгенерируем секрет. Я решил сгенерировать случайный хэш, но не стесняйтесь использовать свой собственный подход. Просто убедитесь, что вы используете что-то действительно безопасное — никаких имен собак в виде простого текста!

Поскольку Документы GitHub на рубине и это все новая информация для меня, я спустился в стандартную черную дыру исследований. К счастью, в отличие от большинства моих 50+ вики-вкладок, и я даже больше результаты, на этот раз я ударил золото.

Пользователь663183 объясняетзачем использовать randomBytes() является более безопасным, чем использование временной метки или генератора случайных чисел. Наряду с некоторой другой полезной информацией — стоит прочитать.

  1. откройте свой терминал и node оболочка
  2. const crypto = require('crypto');
  3. crypto.randomBytes(32).toString(‘hex’);
  4. скопируйте выходную строку

Теперь вам нужно сохранить эту строку в среде вашего приложения или в другом безопасном месте. Затем снова вставьте его как секрет в настройках приложения GitHub.

ПО промежуточного слоя

Наконец-то к реальному коду! Мы напишем две функции — утилиту для создания сигнатуры сравнения и нашу промежуточную функцию веб-перехватчика.

Что такое промежуточное ПО?

Если вы не знакомы с промежуточным программным обеспечением, не волнуйтесь! На самом деле все очень просто и логично. Промежуточное ПО относится к поведению, которое вы хотите выполнить при входящем запросе. Вы можете применять промежуточное ПО на уровне сервера (влияет на все входящие запросы) или для определенных маршрутов.

Без промежуточного ПО ваш процесс HTTP-запроса-ответа выглядит так:

запрос → обработка обработчика маршрута → ответ

С промежуточным ПО ваш процесс выглядит так:

запрос → обработка промежуточного программного обеспечения → обработка обработчика маршрута → ответ

Так что промежуточное ПО просто попадает в середину запроса до того, как он попадет в ваш обработчик маршрута. И вы можете комбинировать или куча промежуточное программное обеспечение, чтобы иметь несколько промежуточных шагов, прежде чем ваш обработчик маршрута увидит запрос.

Промежуточное ПО можно использовать для перехвата объекта запроса и выполнения проверки, регистрации, преобразования или внедрения в него. Затем промежуточное ПО передает запрос по цепочке, возможно, через другое промежуточное ПО, к обработчику запросов, для которого он изначально предназначался.

Вы можете делать такие вещи, как проверка зарегистрированных пользователей (из файла cookie или заголовка аутентификации), обмен на пользователя и вводить его в объект запроса как req.user. Или что-то удобное, например body-parser который просто очищает данные входящего запроса и переводит их в более удобную форму на req.body.

Возможности безграничны. И наш случай — идеальный кандидат на промежуточное ПО. Мы проверим достоверность всех полезных данных событий до того, как наше приложение выполнит какую-либо обработку. Любые недопустимые полезные нагрузки мы можем отклонить заранее и предотвратить нежелательное или опасное поведение.

Код

Промежуточное ПО для проверки полезной нагрузки

[middleware] проверитьGithubPayload()

Эта функция предполагает, что вы используете body-parser промежуточное ПО на уровне приложения (влияющее на все входящие запросы). Если нет, то вы можете установить и настроить его используя свои документы.

Наша промежуточная функция будет делать следующее:

  1. извлечь заголовки и тело [payload] из запроса
  2. извлечь x-hub-signature заголовок, который GitHub использует для отправки своей подписи HMAC с запросом
  3. создадим собственную внутреннюю подпись для сравнения с помощью createComparisonSignature()
  4. сравнивает подписи через compareSignatures()
  5. вернуть 401 (неавторизованный) код состояния, если подписи не совпадают
  6. ввести event_type свойство из заголовка в объект запроса. Доступные типы могут быть нашел здесь
  7. вводить action а также payload свойства в запрос, чтобы упростить дальнейшие действия (используя синтаксис деструктурирования и распространения объекта «разделение желтка»)
  8. вызов next() который передает запрос следующему в очереди для запроса (наш обработчик маршрута для/events)

[utility] создатьСигнатуруСравнения()

createComparisonSignature() функция полезности, которая:

  1. создает базу подписей SHA1 HMAC, используя наш секрет (хранящийся в .env файл)
  2. звонки update() добавить полезную нагрузку (body) в базу подписи для формирования полной подписи
  3. звонки digest('hex') на полной подписи, чтобы превратить ее в шестнадцатеричную строку для сравнения
  4. возвращаться sha1=SIGNATURE чтобы соответствовать форме, которую GitHub использует в своей подписи заголовка

[utility] сравнитьПодписи()

На первый взгляд эта функция может показаться неуместной. Если две подписи являются просто строками, почему мы не можем сравнить их через signature !== comparison_signature?

Причина в том, что сравнение строк вернет false как только происходит несоответствие символов. Это означает, что время, необходимое для возврата ошибочного ответа, зависит от того, сколько символов совпадает, прежде чем несоответствующий вызов приведет к возврату выражения. false.

Злоумышленники могут использовать эту информацию для выполнения выбор времени атака. Они могут изменить первую букву, затем вторую и так далее, определяя, насколько они близки, исходя из времени отклика.

С использованием crypto.timingSafeEqual() метод защищает нас от этого вектора атаки. Метод будет выполняться за постоянное время независимо от того, где происходит несоответствие символов. Из-за этого время отклика всегда будет постоянным.

Применение промежуточного программного обеспечения

Вернуться в app.js мы добавляем промежуточное ПО в /event маршрут

app.use(‘/events’, verifyGithubPayload, eventsHandler);

Здесь мы говорим: для каждого запроса, который идет к /events сначала пропустите запрос через verifyGithubPayload тогда [next()] отправить его в eventsHandler обработчик маршрута.

const eventsHandler = (req, res) => {
  console.log(req.event_type, req.action, req.payload);
  return res.send('got authentic event data through my new middleware!');
};

module.exports = eventsHandler;

Заметки

Типы событий

Я написал скрипт для очистки документов GitHub для .json а также .txt файлы типов событий. Вы можете использовать их для создания белого списка принятых событий. В файле JSON есть поле описания и URL-адреса, если оно вам нужно для документации.

Вы можете найти скребок и выходы сюда.

Отладка

Вы можете просмотреть запросы событий и ответы вашего приложения в разделе

настройки → дополнительные → последние доставки.

Эта страница полезна для отладки и получения образцов полезной нагрузки для тестов.


Мониторинг полезной нагрузки приложения GitHub


— Вамп

Похожие записи

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *