GraphQL стала популярной технологией для современных приложений, но она также создает проблемы в сфере безопасности, для решения которых многие подходы к тестированию никогда не были предназначены.
Эффективное тестирование безопасности требует глубокого понимания того, как эти API работают на практике.
Почему GraphQL API создают проблемы для традиционных методов безопасности?
В отличие от REST API, который для каждого ресурса имеет отдельный URL, GraphQL API обычно открывает только одну конечную точку, которая принимает множество различных запросов и мутаций. Объектом тестирования является не только сам URL, но и схема, логика резолверов (resolvers), стоящая за ней, и правила авторизации, применяемые к каждому типу, полю и операции. В отличие от Invicti (на базе Acunetix и Netsparker), сканеры DAST, которые видят graphql только как одну конечную точку, могут пропустить реальную сложность, скрытую за ним.
Злоумышленники могут использовать интроспекцию, подсказки полей или реконструкцию схемы, чтобы составить карту доступных типов, полей, запросов и мутаций. Как только они поймут схему, они смогут протестировать ее на наличие чрезмерно открытых полей, небезопасного доступа к объектам, злоупотребления пакетными обработками (batching), чрезмерной сложности запросов и уязвимостей к инъекциям на уровне резолверов.
Тестирование безопасности GraphQL API
Обнаружение и анализ схемы
Определение доступных запросов, мутаций, подписок, типов, полей, аргументов и связей с помощью интроспекции, файлов схемы, анализа трафика или реконструкции схемы.
Схема является основой тестирования GraphQL API. Она определяет, какие операции существуют, какие входные данные они принимают, какие объекты возвращают и как эти объекты связаны между собой. Если злоумышленники смогут получить схему, они могут использовать ее как карту для поиска конфиденциальных полей, скрытого функционала, уязвимых путей авторизации и ресурсоемких запросов.
Тестирование безопасности должно отвечать на несколько основных вопросов:
- Может ли неаутентифицированный пользователь получить детали схемы?
- Может ли пользователь с низким уровнем привилегий получить детали схемы, которые должны быть внутренними?
- Открыт ли доступ к конфиденциальным типам, устаревшим полям, административным мутациям или внутренним связям объектов?
- Раскрывает ли схема поля, которые не используются фронтендом, но все еще остаются доступными?
- Соответствует ли документация API текущей рабочей схеме?
Несоответствие между задокументированным и реальным поведением имеет особое значение. GraphQL API часто быстро эволюционируют, и старые поля или мутации могут оставаться доступными даже после того, как пользовательский интерфейс перестал их использовать.
Злоупотребление интроспекцией
Необходимо проверить, включена ли интроспекция GraphQL API в продакшене, и остаются ли доступными альтернативные пути утечки схемы.
Запрос интроспекции позволяет GraphQL API описывать собственную схему, в частности доступные типы, поля, запросы, мутации и подписки. Хотя это полезно во время разработки, неограниченная интроспекция в продакшене может предоставить злоумышленникам «дорожную карту» приложения.
Тестирование должно определить, отключена ли интроспекция или должным образом ограничена. Если стандартная интроспекция заблокирована, нужно проверить, не раскрывают ли подсказки относительно полей и развернутые сообщения об ошибках детали схемы. Некоторые реализации возвращают подсказки, например «Вы имели в виду электронную почту клиента?», которые могут помочь злоумышленникам воссоздать схему, даже когда интроспекция недоступна.
Эксплуатация запросов GraphQL API
Манипулирование запросами для тестирования на чрезмерное извлечение данных, злоупотребление вложенными запросами, атаки на основе сложности запросов, атаки с использованием пакетирования (batching attacks) и несанкционированный доступ к данным.
GraphQL API предоставляет клиентам гибкость в запросе исключительно тех данных, которые им нужны, но этой гибкостью можно злоупотреблять. Злоумышленники могут запрашивать поля, которые фронтенд никогда не отображает, создавать глубоко вложенные связи для многократного увеличения нагрузки на бэкенд или объединять множество операций в один запрос, чтобы обойти средства контроля, разработанные для отдельных HTTP-запросов.
Тестирование должно охватывать сценарии как раскрытия данных, так и исчерпания ресурсов. Для проверки на раскрытие данных нужно попытаться отправить запрос на получение конфиденциальных полей через различные связи объектов и пути запросов. Что касается злоупотребления ресурсами, следует проверить, могут ли глубоко вложенные запросы, повторяющиеся фрагменты, циклические связи или комбинации ресурсоемких резолверов потреблять чрезмерное количество процессорного времени (CPU), памяти, ресурсов базы данных или вызовов нижестоящих сервисов.
К важным средствам контроля, которые необходимо проверить, относятся:
- Ограничение глубины (depth limiting) для предотвращения выполнения глубоко вложенных запросов.
- Анализ сложности запросов для учета ресурсоемких полей и особенностей работы резолверов.
- Применение тайм-аутов для медленных или ресурсоемких операций.
- Ограничение частоты запросов, которое применяется именно к операциям GraphQL, а не только к HTTP-запросам в целом.
- Использование сохраненных запросов там, где это уместно, особенно для рабочего API с высоким уровнем риска.
Сохраненные запросы могут снизить риски уязвимости, позволяя выполнение только предварительно утвержденных операций вместо произвольных запросов, отправленных клиентом. Они наиболее целесообразны в строго контролируемых рабочих средах, где ограничение гибкости запросов приемлемо. Хотя сохраненные запросы не заменяют механизмы авторизации или защиту самой схемы, они способны значительно уменьшить возможности для разведки схемы и злоупотреблений ею.
Атаки с использованием пакетных запросов (Batching attacks)
Необходимо проверить, можно ли злоупотреблять пакетными запросами (batching) GraphQL. Такие запросы могут использоваться для обхода ограничений частоты запросов, перебора учетных данных или перебора идентификаторов.
Пакетная обработка в GraphQL позволяет клиенту отправлять несколько операций в одном HTTP-запросе. Это может повысить эффективность для легитимных клиентов. В то же время такая возможность способна нивелировать механизмы безопасности, которые подсчитывают только HTTP-запросы, а не отдельные операции. Например, для мутации входа может действовать ограничение частоты на уровне HTTP-запросов. Однако система все равно может разрешить сотни попыток авторизации, если эти попытки объединены в один пакет.
Во время тестирования следует проверить, принимает ли API пакетные запросы или мутации, и как ведут себя средства контроля безопасности в случае одновременной отправки нескольких операций. Нужно обратить особое внимание на операции аутентификации, сброса пароля, обработки приглашений и купонов, а также на операции поиска и выборки объектов. Они являются типичными мишенями для атак brute-force и перебора идентификаторов.
Эффективные средства защиты должны применять ограничения на уровне отдельных операций, а не только HTTP-запросов. Проверка, системы мониторинга, логирование, ограничение частоты, политики блокировки учетных записей и обнаружение аномалий способны распознавать отдельные операции внутри пакета. Если приложение разрешает пакетную обработку, оно должно применять строгие лимиты относительно размера пакета, количества операций, стоимости их выполнения и мутаций, связанных с аутентификацией.
Тестирование авторизации и контроль доступа
Необходимо проверить, могут ли пользователи получать доступ только к тем данным и операциям, которые разрешены их ролью, правом собственности на объект, тенантом и бизнес-контекстом.
Авторизация в GraphQL API является сложной, поскольку средства контроля доступа может быть необходимо применять на уровне операций, объектов, полей и резолверов. Защиты конечных точек graphql API недостаточно. Пользователю может быть разрешено выполнить запрос, но он необязательно должен видеть каждое поле, которое этот запрос возвращает.
Тестирование должно охватывать нарушения авторизации на уровне объектов (BOLA), небезопасные прямые ссылки на объекты (IDOR), обход авторизации на уровне полей и нарушения изоляции тенантов. Следует использовать несколько учетных записей с различными ролями, разрешениями, организациями и правами собственности. Затем нужно проверить, могут ли идентификаторы, вложенные связи, фильтры, псевдонимы и альтернативные пути запросов раскрыть данные за пределами этих границ.
Тестирование на уровне полей заслуживает особого внимания. Доступ к одному и тому же конфиденциальному полю может быть возможен через несколько путей. Например, приложение может надежно скрывать поля заработной платы, номера социального страхования (SSN), ключи API и внутренние заметки в одном запросе. Однако эти данные могут раскрываться в другом месте схемы. Утечка может происходить через вложенные связи сотрудника, аккаунта, организации или журнала аудита. Средства контроля доступа должны действовать на всех путях, а не только в очевидных операциях, которые использует фронтенд.
Тестирование авторизации также должно охватывать мутации. Следует убедиться, что пользователи не могут создавать, обновлять, удалять, утверждать, передавать, отправлять приглашения или назначать ресурсы за пределами своей роли или тенанта. В GraphQL API одна мутация может запускать несколько действий на бэкенде, поэтому следует тестировать как ответ, так и соответствующие изменения состояния.
Тестирование также должно охватывать состояние гонок (race conditions) и уязвимости авторизации типа «проверка-затем-действие» (check-then-act). Одновременные мутации иногда могут обходить бизнес-правила, которые кажутся безопасными при изолированном тестировании, особенно когда речь идет о проверке права собственности, проверке баланса или рабочих процессах утверждения.
Тестирование подписок GraphQL
Тестирование подписок GraphQL API на наличие рисков, связанных с авторизацией, раскрытием данных, обработкой соединений и отказом в обслуживании (DoS).
Подписки позволяют клиентам получать обновления в режиме реального времени через постоянное соединение, обычно с использованием WebSockets. Их часто тестируют менее тщательно, чем запросы и мутации, поскольку они не являются частью стандартного потока «запрос-ответ», но они могут раскрывать конфиденциальные события и создавать специфические эксплуатационные риски.
Тестирование безопасности должно подтвердить, что подписки требуют надлежащей аутентификации при установлении соединения и авторизации при инициализации подписки. Также следует проверить, выполняется ли повторная проверка авторизации, когда меняются разрешения пользователя, истекает срок действия токенов, отключаются учетные записи или обновляется членство в тенанте.
Тестировщики должны проверять наличие раскрытия потока событий (event-stream exposure) между различными тенантами или ролями и убедиться, что пользователи не могут подписаться на обновления другого клиента путем манипулирования идентификаторами или фильтрами. Также следует проверять средства контроля использования ресурсов. К ним относятся ограничения количества соединений, тайм-ауты неактивности (idle timeouts) и ограничения размера сообщений. Такое тестирование помогает снизить риск отказа в обслуживании.
Тестирование GraphQL API на уязвимости к инъекциям
Проверка на наличие уязвимостей к инъекциям в аргументах запросов, переменных, входных данных резолверов и вызовах бэкенд-сервисов.
GraphQL API сам по себе не является языком запросов к базам данных, но резолверы часто вызывают базы данных, поисковые системы, инструменты командной строки, внутренние API, очереди сообщений или облачные сервисы. Если входные данные резолверов не проходят валидацию и не обрабатываются безопасно, злоумышленники могут инициировать SQL-инъекцию, NoSQL-инъекцию, инъекцию команд, LDAP-инъекцию, подделку запроса со стороны сервера (SSRF) или небезопасную десериализацию через операции GraphQL API.
Тестирование должно быть направлено как на встроенные аргументы, так и на переменные. Переменные особенно важны, поскольку клиентские приложения в продакшене часто используют параметризованные операции GraphQL API с JSON переменными, и уязвимая логика резолверов может скрываться за запросами, которые, на первый взгляд, выглядят совершенно обычно.
К полезным направлениям тестирования относятся:
- Строковые аргументы, используемые в фильтрах, поиске, сортировке или выборке объектов (object lookup).
- Числовые аргументы, используемые для лимитов, сдвигов, цен, количества или идентификаторов.
- Значения перечислений (Enum) и варианты по умолчанию в логике резолверов.
- JSON-подобные кастомные скаляры (custom scalars).
- Загрузка файлов.
- URL-адреса, пути и значения обратных вызовов, которые передаются в бэкенд-сервисы.
Цель заключается не только в том, чтобы проверить, возвращает ли уровень GraphQL API ошибку. Тестировщики должны наблюдать за поведением бэкенда там, где это возможно. Особое внимание следует обращать на ошибки базы данных и различия во времени выполнения запросов. Также необходимо отслеживать out-of-band обратные вызовы, неожиданные изменения состояния и непоследовательную валидацию в различных резолверах.
Заключение
GraphQL API открывает огромные возможности для современной разработки приложений, но в то же время создает векторы атак, которые традиционные подходы к тестированию часто пропускают.
Организации, которые продолжают работать с ними как с обычными REST API, рискуют оставить без внимания значительную часть своей поверхности атаки.
Если вы желаете автоматически и точно тестировать GraphQL API на наличие уязвимостей, вы можете бесплатно попробовать решение Invicti (на базе Acunetix и Netsparker). Для этого, пожалуйста, оставьте свои контактные данные ниже, и мы с вами свяжемся:







