Услуги
Аудит информационной безопасности

Аудит информационной безопасности компании – многоплановая задача, в
которую входит несколько направлений анализа
ИТ-инфраструктуры и оценки защищенности приложений и устройств

Подробнее
Решения
Решения

Аккумулировав весь опыт Digital Security, мы подготовили отдельные решения по аудиту безопасности. Каждое решение объединяет специализированные услуги для конкретной отрасли бизнеса или сферы исследования. Вы можете выбрать любую из них или несколько — в комплексе

О нас
Digital Security

Практическая информационная безопасность – наша специализация и любимое дело. Уже 18 лет мы помогаем нашим клиентам уберечь сервисы и системы от киберугроз, а также активно участвуем в развитии мирового ИБ-сообщества

Подробнее
Экспертиза
Экспертиза

Для клиентов Digital Security работает команда экспертов ИБ мирового уровня. Мы получаем благодарности за вклад в обеспечение безопасности от лидеров ИТ-индустрии, а также подтверждаем свои навыки международными сертификатами в области ИБ

Подробнее
Ресурсы
Ресурсы

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

Подробнее

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

Посмотреть раздел

Масштабные исследования и аналитические обзоры. Описания хакерских инструментов и техник

Статьи и аналитические работы, полезные для бизнеса. Экспертное мнение об актуальных проблемах информационной безопасности

Видеоархив наших вебинаров и конференций. А также анонсы предстоящих мероприятий

Руководства, презентации, чек-листы. Гайды для повышения уровня осведомленности в вопросах ИБ

Наверх

Пентест приложений с GraphQL

25.03.2019 /
20 мин
Прочитать позже

    Отправим материал на:

    В последнее время GraphQL набирает всё большую популярность, а вместе с ней растёт и интерес со стороны специалистов информационной безопасности. Технологию используют такие компании, как: Facebook, Twitter, PayPal, Github и другие, а это значит, что пора разобраться, как тестировать такое API. В этой статье мы расскажем о принципах этого языка запросов и направлениях тестирования на проникновение приложений с GraphQL.

    Зачем нужно знать GraphQL? Этот язык запросов активно развивается и всё больше компаний находят ему практическое применение. В рамках программ Bug Bounty популярность этого языка тоже растёт, интересные примеры можно посмотреть здесьздесь и здесь.

    Подготовка

    Тестовая площадка, на которой вы найдете большинство примеров, приведённых в статье.
    Список с приложениями, которые вы тоже можете использовать для изучения.
    Для взаимодействия с различными API лучше использовать IDE для GraphQL:

    Последнее IDE мы и рекомендуем: в Insomnia удобный и простой интерфейс, есть множество настроек и автодополнение полей запросов.

    Перед тем, как перейти непосредственно к общим методам анализа безопасности приложений c GraphQL, вспомним основные понятия.

    Что такое GraphQL?

    GraphQL — язык запросов для API, призванный обеспечить более эффективную, мощную и гибкую альтернативу REST. В его основе лежит декларативная выборка данных, то есть клиент может точно указать, какие именно данные ему нужны от API. Вместо нескольких конечных точек API (REST) GraphQL представляет единую конечную точку, которая предоставляет клиенту запрашиваемые данные.

    Основные различия между REST и GraphQL

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

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

    Виды запросов

    В GraphQL существует 3 основных вида запросов:

    • Query
    • Mutation
    • Subscription

    Query
    Запросы Query используются для получения/чтения данных в схеме.
    Пример такого запроса:

    query {
      allPersons {
        name
      }
    }
    

    В запросе указываем, что хотим получить имена всех пользователей. Помимо имени мы можем указать и другие поля: ageidposts и др. Чтобы узнать, какие именно поля мы можем получить, нужно нажать Ctrl+Пробел. В данном примере мы передаём параметр, с которым приложение вернёт первые две записи:

    query {
      allPersons(first: 2) {
        name
      }
    }

    Mutation
    Если тип query нужен для чтения данных, то тип mutation нужен для записи, удаления и изменения данных в GraphQL.
    Пример такого запроса:

    mutation {
      createPerson(name:"Bob", age: 37) {
        id
        name
        age
      }
    }

    В этом запросе мы создаём пользователя с именем Bob и возрастом 37 (эти параметры передаются как аргументы), во вложении (фигурных скобках) указываем, какие данные мы хотим получить от сервера после создания пользователя. Это нужно для того, чтобы понять, что запрос выполнен успешно, а также для получения данных, которые сервер генерирует самостоятельно, такие как id.

    Subscription
    Ещё один вид запросов в GraphQL — subscription. Он нужен для оповещения пользователей о каких-либо изменениях, произошедших в системе. Работает это так: клиент подписывается на какое-то событие, после чего с сервером устанавливается соединение (обычно через WebSocket), и, когда это событие происходит, сервер отсылает клиенту уведомление по установленному соединению.

    Пример:

    subscription {
      newPerson {
        name
        age
        id
      }
    }

    Когда создастся новый Person, сервер отошлёт клиенту информацию. Наличие запросов subscription в схемах встречается реже, чем query и mutation.
    Стоит отметить, что все возможности по query, mutation и subscription создаёт и настраивает разработчик конкретной API.

    Факультатив

    На практике разработчики часто используют alias и OperationName в запросах для внесения ясности.

    Alias
    GraphQL для запросов предусматривает возможность alias, которая может облегчить понимание того, что именно запрашивает клиент.
    Предположим, что у нас есть запрос вида:

    {
      Person(id: 123) {
        age
      }
    }

    который выведет имя пользователя с id 123. Пусть имя этого пользователя будет Vasya.
    Чтобы в следующий раз не ломать голову, что выведет данный запрос, можно сделать вот так:

    {
      Vasya: Person(id: 123) {
        age
      }
    }
    

    OperationName

    Помимо alias в GraphQL используется OperationName:

    query gettingAllPersons {
      allPersons {
        name
        age
      }
    }

    OperationName нужен для пояснения того, что именно делает запрос.

    Пентест

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

    POST /simple/v1/cjp70ml3o9tpa0184rtqs8tmu/ HTTP/1.1
    Host: api.graph.cool
    User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:65.0) Gecko/20100101 Firefox/65.0
    Accept: */*
    Accept-Language: ru-RU,ru;q=0.8,en-US;q=0.5,en;q=0.3
    Accept-Encoding: gzip, deflate
    Referer: https://api.graph.cool/simple/v1/cjp70ml3o9tpa0184rtqs8tmu/
    content-type: application/json
    Origin: https://api.graph.cool
    Content-Length: 139
    Connection: close
    
    {"operationName":null,"variables":{},"query":"{\n  __schema {\n    mutationType {\n      fields {\n        name\n      }\n    }\n  }\n}\n"}

    Некоторые параметры, по которым можно понять, что перед вами GraphQL, а не что-то иное:

    • в теле запроса имеются слова: __schema, fields, operationName, mutation и др.;
    • в теле запроса много символов «\n». Как показывает практика, их можно убрать, чтобы было удобнее читать запрос;
    • часто путь отправки запроса на сервер: ⁄graphql

    Отлично, нашли и определили. Но куда вставлять кавычку как узнать, с чем нам нужно работать? На помощь придёт интроспекция.

    Интроспекция

    В GraphQL предусмотрена схема интроспекции, т.е. схема с описанием данных, которые мы можем получить. Благодаря этому мы можем узнать, какие запросы существуют, какие аргументы им можно/нужно передавать и многое другое. Заметим, что в отдельных случаях разработчики намеренно не разрешают возможность интроспекции их приложения. Тем не менее, основное большинство всё же оставляет такую возможность.

    Рассмотрим основные примеры запросов.

    Пример 1. Получение всех видов запросов

    query {
      __schema {
        types {
          name
          fields {
            name
          }
        }
      }
    }
    

    Формируем запрос query, указываем, что хотим получить данные по __schema, а в ней типы, их имена и поля. В GraphQL существуют служебные имена переменных: __schema, __typename, __type.

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

    Пример 2. Получение полей для конкретного вида запроса (query, mutation, description)

    query {
      __schema {
        queryType {
          fields {
            name
            args {
              name
            }
          }
        }
      }
    }

    Ответом на данный запрос будут все возможные запросы, которые мы можем выполнить к схеме для получения данных (вид query), и возможные/необходимые аргументы для них. Для некоторых запросов указание аргумента(ов) обязательно. Если выполнить такой запрос без указания обязательного аргумента, сервер должен выдать сообщение с ошибкой, что необходимо его указать. Вместо queryType мы можем подставлять mutationType и subscriptionType для получения всех возможных запросов по мутациям и подпискам соответственно.

    Пример 3. Получение информации о конкретном типе запроса

    query {
      __type(name: "Person") {
        fields {
          name
        }
      }
    }

    Благодаря такому запросу мы получим все поля для типа Person. В качестве аргумента вместо Person мы можем передавать любые другие имена запросов.

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

    Information disclosure

    Чаще всего приложение, использующее GraphQL, состоит из множества полей и типов запросов, и, как известно многим, чем сложнее и больше приложение, тем сложнее его настраивать и следить за его безопасностью. Именно поэтому при тщательной интроспекции можно найти что-нибудь интересное, например: ФИО пользователей, номера их телефонов и другие критичные данные. Поэтому если вы хотите найти что-то подобное, то рекомендуем проверять все возможные поля и аргументы приложения. Так в рамках пентеста в одном из приложений были обнаружены данные пользователей: ФИО, номер телефона, дата рождения, некоторые данные карт и пр.

    Пример:

    query {
      User(id: 1) {
        name
        birth
        phone
        email
        password
      }
    }
    

    Перебирая значения id, мы сможем получить информацию о других пользователях (а, может, и нет, если всё настроено правильно).

    Injections
    Стоит ли говорить, что практически везде, где есть работа с большим объёмом данных, есть и базы данных? А где есть БД — там могут быть и SQL-injections, NoSQL-injections и другие виды инжектов.

    Пример:

    mutation {
      createPerson(name:"Vasya'--+") {
        name
      }
    }

    Здесь элементарная SQL-инъекция в аргументе запроса.

    Authorization bypass
    Допустим, мы можем создавать пользователей:

    mutation {
      createPerson(username:"Vasya", password: "Qwerty1") {
      }
    }

    Предположив, что есть некий параметр isAdmin в обработчике на сервере, мы можем отправить запрос вида:

    mutation {
      createPerson(username:"Vasya", password: "Qwerty1", isAdmin: True) {
      }
    }

    И сделать пользователя Vasya администратором.

    DoS

    Помимо заявленного удобства в GraphQL есть и свои недочеты с точки зрения безопасности.

    Рассмотрим пример:

    query {
      Person {
        posts {
          author {
            posts {
              author {
                posts {
                  author ...
                }
              }
            }
          }
        }
      }
    }

    Как видите, мы создали зацикленный вложенный запрос. При большом количестве таких вложений, например, в 50 тыс., мы можем отправить запрос, который будет очень долго обрабатываться сервером или вообще «уронит» его. Вместо обработки валидных запросов сервер будет занят распаковкой гигантской вложенности запроса-пустышки.

    Помимо большой вложенности, запросы сами по себе могут быть «тяжелыми» — это когда у одного запроса масса полей и внутренних вложений. Такой запрос тоже может вызвать затруднения в обработке на сервере.

    Вывод

    Итак, мы рассмотрели основные принципы тестирования на проникновение приложений с GraphQL. Надеемся, вы узнали что-то новое и полезное для себя.

    Если вам интересна данная тема, и вы хотите изучить её глубже, то рекомендуем следующие ресурсы:

    Больше интересных статей

    Подпишитесь на наши обновления

    Узнавайте все новости первыми

      Мы используем куки. Никогда такого не было, объясните

      ОК