В этой статье я хочу рассказать о своем исследовании, посвященном tRPC. Сначала мы рассмотрим концепции tRPC, а затем перейдем к анализу поверхности атаки приложения tRPC.
tRPC с точки зрения разработчика
tRPC расшифровывается как "TypeScript Remote Procedure Call" и использует возможности TypeScript для обеспечения безопасности типов на границах клиент-сервер. Это означает, что tRPC позволяет разработчикам создавать API, в которых входные и выходные данные автоматически проверяются на соответствие типам, что значительно снижает риск возникновения ошибок во время выполнения из-за несовпадения типов данных или неожиданных структур данных.
Давайте узнаем, как это работает:
Определение API
Как разработчик, вы должны определить API на сервере с помощью TypeScript, указав типы ввода и вывода для функций, которые представляют конечные точки API. Эти функции, известные в tRPC как процедуры, могут выполнять различные операции, такие как получение данных (Queries), а также создание, удаление и обновление данных (Mutations).
Определение маршрутизаторов
На сервере процедуры организованы в маршрутизаторы. Маршрутизаторы управляют различными путями и операциями API и могут быть вложены для работы с более сложными API, создавая структурированный подход, подобный контроллерам в традиционном API. Вот пример:
В этом примере определены две процедуры: одна для чтения данных, другая - для их изменения. Возможно, что некоторые процедуры могут получать входные данные от клиента.
Интеграция на стороне клиента
tRPC автоматически генерирует клиентскую библиотеку на основе типов вашего API. Эта клиентская библиотека позволяет внешнему приложению вызывать серверные процедуры напрямую, как локальные функции, без необходимости беспокоиться о HTTP-методах, заголовках или путях. Когда вы вызываете серверную процедуру, типы выводятся непосредственно из определений TypeScript сервера. Это означает, что разработчик получает автозаполнение предложений в редакторе и проверку типов во время компиляции, что помогает предотвратить проблемы, связанные с предоставлением неправильного типа или структуры данных в запросах или ответах API. Вот пример:
Клиент вызывает метод 'addUser'; результат этого метода считывается из ответа и отображается в DOM. Ссылка httpBatchLink особенно полезна в сценариях, когда клиенту необходимо отправить несколько запросов на сервер одновременно. В качестве альтернативы httpLink можно использовать для выполнения стандартных отдельных HTTP-запросов от клиента к серверу tRPC.
Обмен данными между клиентом и сервером (транспортный уровень)
tRPC использует транспортные механизмы (обычно HTTP/HTTPS).
До сих пор мы изучали концепции tRPC. Далее мы рассмотрим tRPC с точки зрения исследователя.
tRPC с точки зрения исследователя
Чтобы выявить уязвимости в tRPC, мы должны принять во внимание следующие шаги:
Определение стиля tRPC
Исследование tRPC
Анализ поверхности атаки
Шаг 1: Определение tRPC
Самый важный вопрос, который мы должны решить при тестировании API, это какой стиль используется. Каждый API имеет свой стиль. Поняв стиль, мы сможем легче провести разведку и обнаружить уязвимости. Как мы можем определить, что стиль API цели - tRPC?
Как мы видели в предыдущем разделе, процедуры в tRPC бывают двух видов: Query и Mutation. Метод GET используется для Querie, которые предполагают чтение данных, а метод POST - для Mutation, то есть для изменения данных. У нас есть следующие общие шаблоны:
GET /ProcedureName
POST /ProcedureName
GET /getUsers
POST /addUser
Иногда приложение использует httpBatchLink для отправки всех запросов вместе. В этом случае в строку запроса включается параметр 'batch':
GET /getUsers?batch=1
Формат ошибки
Еще один способ определить tRPC - понаблюдать за тем, какую ошибку возвращает сервер для различных состояний. Если процедура не была определена для конечной точки на стороне сервера, изменение метода HTTP приведет к ошибке со стороны сервера:
Изменение типа параметра
Изменение типов параметров в запросе и выдача ошибки могут помочь нам в определении tRPC:
Формат данных запроса/ответа
Формат данных, который мы можем использовать в tRPC, - это JSON. В примере ниже показана структура данных в двух сценариях: httpLink и httpBatchLink:
Комбинируя вышеописанные техники, мы можем определить tRPC.
Шаг 2: tRPC Recon
Во время разведки API доступ к документации может помочь нам в анализе цели. Наша цель может сделать свой API общедоступным, к которому мы можем получить доступ, используя различные техники. Цель может использовать панель trpc. Этот инструмент предназначен специально для документирования конечных точек tRPC.
Вот простой вариант реализации:
Разработчик может определить промежуточное ПО для панели trpc и указать конечную точку для доступа к панели, как показано в примере ниже:
Если мы отправим запрос на "/panel", то сможем просмотреть список всех процедур и получить полный доступ к документации, что упростит нам процесс поиска уязвимостей. Эта конечная точка может отличаться в разных целях. Лучший способ найти эту конечную точку - использовать фаззинг и методы, описанные ниже:
Google Dorking
site:TARGET.tld intitle:"tRPC.panel()" inurl:/panel
site:TARGET.tld intitle:"tRPC.panel()"
API Fuzzing
id: trpc-panel
info:
name: Public trpc-panel
author: LogicalHunter
severity: info
tags: exposure,trpc
http:
- method: GET
path:
- "{{BaseURL}}/panel"
- "{{BaseURL}}/trpc-panel"
- "{{BaseURL}}/trpc"
- "{{BaseURL}}/trpc/panel"
- "{{BaseURL}}/api/panel"
- "{{BaseURL}}/api/trpc-panel"
- "{{BaseURL}}/docs"
- "{{BaseURL}}/doc"
- "{{BaseURL}}/api/docs"
- "{{BaseURL}}/api/doc"
- "{{BaseURL}}/api/trpc/panel"
headers:
Accept: text/html
stop-at-first-match: true
matchers-condition: and
matchers:
- type: word
part: body
words:
- "tRPC.panel()"
- type: status
status:
- 200
Сторонние источники
Иногда API сайта может быть в открытом доступе для других разработчиков. В таких случаях мы можем обратиться за помощью к нижеупомянутым сайтам:
https://www.postman.com/explore
https://apis.guru/
https://github.com/public-apis/public-apis
https://rapidapi.com/search/
Анализ атаки
Нам необходимо рассмотреть два общих сценария:
Документация доступна
Документация недоступна
Документация цели доступна
В этом случае, имея доступ к документации, мы можем просмотреть все процедуры и структуру запросов и ответов. На данном этапе достаточно изучить различные уязвимости с помощью документации. Первый вопрос, который мы должны задать: Как работает процесс аутентификации в API и какая конечная точка используется? Чтобы получить доступ к различным конечным точкам, мы должны быть авторизованы в системе. В этом случае нам необходимо определить процедуру, которая позволяет нам войти в систему:
POST /AuthProcedure
POST /authUser
При изучении документации необходимо исследовать различные компоненты, каждый из которых может выявить различные уязвимости:
Затем мы должны рассмотреть следующий вопрос: Какие уязвимости существуют в различных процедурах? На этом этапе, в зависимости от того, является ли операция Query или Mutation, мы пытаемся определить различные уязвимости. Для Query и Mutation мы должны изучить следующие классы уязвимостей:
Документация по цели недоступна
Во втором сценарии, при отсутствии доступа к документации, нам необходимо составить карту целевого API. Для этой задачи можно использовать такие инструменты, как Logger++. Как правило, мы просматриваем приложение и отфильтровываем протоколируемые запросы tRPC с помощью Logger++. Затем мы можем экспортировать конечные точки и приступить к поиску различных уязвимостей.
После выявления открытых конечных точек цели нам необходимо определить скрытые конечные точки приложения. Для этого необходимо провести фаззинг известной конечной точки. Обнаружение скрытых конечных точек может помочь нам найти различные уязвимости, например, связанные с контролем доступа. Общая схема выглядит следующим образом:
Fuzz(METHOD) /FUZZ(ProcedureName)
POST /addUsers
Possible Endpoints:
METHOD /getUsers
METHOD /deleteUsers
METHOD /updateUsers
Более того, для практического опыта я создал уязвимое приложение tRPC, доступ к которому можно получить через мой GitHub.
Надеюсь, вам понравилось читать эту статью.
19Zb84
А trpc - client можно сгенерировать на основе openAPI спеки ?
Я использую в проекте swagger client который генерирует апи на основе спеки и он так же обеспечивает безопасность типов на границах клиент-сервер.
У нас openAPI спека является обязательной для бэкенда.
На бэкенде пока node.js не используется, но даже если будет, что мешает его написать с использованием typeScript ? При этом оставив на клиенте swagger client на основе openAPI спеки, которая в любом случае будет составляться.
Мне хочется понять, в чем плюсы использования tRPC. Например я захочу его использовать. В чём я могу выиграть ?
Мне нравится swagger client потому что
1. Он экономит очень время. За счет автоматической генерации слиента.
2. Есть валидация запросов. Обеспечение безопасности типов на границах клиент-сервер.
3. Есть версионирование api на клиенте относительно спеки, которая используется на бэкенде в данный момент. Не возникает проблем при разработке в расхождении в запросах.
4. нет привязки к TypeScript. Если надо он используется, если нет то не используется. Например, на бэкенде может потребоваться AssemblyScript, тогда может быть будет актуально написать остальную часть сервера на js.
dndred
tRPC предназначен и очень удобен в монолитных фулл стак приложениях, например на основе Next.JS. Он позволяет быстро и без лишних действий писать API с проверкой типов с помошью TypeScript. Райнтайм валидацию так же можно использовать, но это не обязательно.
trpc не генерирует никакого клиента. Вернее клиент есть и очень удобный, но весь он существует только в рамках TypeScript.
Простой резолвер выглядит так:
После этого на клиенте можно сразу писать
Если же на клиенте вызвать отсутствующий роут
То, браузер послушно отправит запрос на
/api/trpc/cart.test_RANDOM_STRING
. Но TypeScript поймает это в билд тайм и в прод ошибка не уйдёт.Если у вас фулл стак монолит, то получается все преимущества Swagger со сгенерированным клиентом, только намного удобнее.
19Zb84
То же самое что вы описали делается с тем же свагер клиентом, который я описал.
Причем здесь монолит ?
Я правильно понимаю. Эта библиотека именно под next.js тогда в основном и ориентированна ?
dndred
Swagger (OpenAPI) это стандарт описания схемы API который позволяет соединять разные системы. В том числе генерировать клиент.
Но часто бывает, что у нас одна кодовая база. Тогда описание схемы Swagger может быть излишним. tRPC решает эту задачу проще и удобнее.
Нет, не обязательно. Просто это типичный пример когда бэк и фронт существуют вместе. В одной кодовой базе, на одном языке.
19Zb84