Назад в блог
API Security4.07.2026

Безопасность API GraphQL: интроспекция, DoS, BOLA и укрепление резолверов

GraphQL заменяет десятки REST-эндпоинтов единой гибкой поверхностью запросов. Именно эта гибкость ломает предположения безопасности, которые команды переносят из REST. Один POST на /graphql может обходить произвольные графы объектов, запрашивать вложенные связи и упаковывать сотни операций в один запрос. Если авторизация, rate-limiting и контроль стоимости проектировались по эндпоинтам, здесь они не работают. Этот материал разбирает главные специфичные для GraphQL риски и серверные меры, которые реально их снижают.

Утечка интроспекции

GraphQL самоописателен. Один запрос интроспекции __schema возвращает каждый тип, поле, аргумент, мутацию и устаревшее поле. Для атакующего это бесплатная и достоверная карта всей поверхности атаки — включая внутренние администраторские мутации и поля, которые вы считали скрытыми.

Интроспекция — легитимный инструмент разработчика, но в проде она отдаёт разведку любому. Отключите её в продакшн-сборках (например introspection: false в Apollo Server) и отключите IDE Playground/GraphiQL в публичных развёртываниях. Помните: отключение интроспекции — это defense-in-depth, а не авторизация — инструменты вроде Clairvoyance восстанавливают схему по подсказкам полей, поэтому отключите и сообщения-подсказки и никогда не полагайтесь на секретность схемы как границу безопасности.

Глубина и сложность запросов: неаутентифицированный DoS

Поскольку форму запроса выбирает клиент, он может создавать глубоко вложенные циклические запросы, взрывающие работу сервера. Если у Author есть posts, а у Post есть author, атакующий рекурсивно спускается author→posts→author→posts на произвольную глубину, вызывая экспоненциальное выполнение резолверов и нагрузку на БД из одного небольшого запроса. Это соответствует OWASP API4:2023 Unrestricted Resource Consumption и является классическим асимметричным DoS.

Применяйте послойно:

  • Ограничение глубины — отклоняйте запросы выше фиксированной вложенности (например graphql-depth-limit 7–10).
  • Анализ сложности — назначьте стоимость каждому полю и отклоняйте запросы выше бюджета до выполнения (graphql-cost-analysis, graphql-query-complexity).
  • Лимиты пагинации — принудительно ограничивайте first/last; никогда не допускайте безграничных списков.
  • Таймауты и лимиты количества — ограничивайте время выполнения и число возвращаемых узлов.
  • Persisted queries — разрешайте только утверждённый список документов запросов в проде, полностью устраняя произвольные формы.

Злоупотребление батчингом

GraphQL позволяет отправить массив операций в одном HTTP-запросе, а алиасинг позволяет клиенту повторить одно поле многократно под разными именами. Оба обходят наивный rate-limiting по запросам. Один запрос с 1000 алиасированных мутаций login обнуляет защиту от перебора:

  • mutation { a: login(pw:"1"){token} b: login(pw:"2"){token} ... }

Ограничивайте число операций на запрос, число алиасов для чувствительных полей и применяйте rate-limiting по стоимости операций, а не по числу HTTP-запросов. Применяйте объектный троттлинг к аутентификации и другим склонным к злоупотреблению мутациям.

BOLA / авторизация на уровне объекта в резолверах

Broken Object Level Authorization (OWASP API1:2023) — самая разрушительная и частая уязвимость GraphQL. Запрос { invoice(id: "1042") { total, customer { email } } } вернёт счёт чужого арендатора, если резолвер выбирает по ID без проверки владения. GraphQL усугубляет это, потому что авторизацию нужно применять в каждом резолвере на каждом пути — поле, достижимое через три разных запроса, требует проверки во всех трёх.

Не авторизуйте на уровне HTTP. Применяйте авторизацию на уровне поля и объекта внутри резолверов или выделенного слоя авторизации: проверяйте, что аутентифицированный субъект имеет доступ к конкретному экземпляру объекта, а не только к типу. Переносите идентичность в контексте и предпочитайте движок политик (graphql-shield, OSO или явные guard-ы), чтобы проверки были централизованы и тестируемы. Считайте каждое поле напрямую достижимым и по умолчанию отказывайте.

Инъекции через резолверы

GraphQL ничего не санитизирует — он передаёт аргументы прямо в резолверы, которые часто строят SQL, NoSQL или команды ОС. SQL-инъекция (CWE-89), NoSQL-инъекция и SSRF живут в коде резолверов. Поскольку язык запросов выглядит структурным, разработчики ошибочно считают ввод безопасным. Всегда используйте параметризованные запросы, валидируйте кастомные скаляры и считайте каждый аргумент недоверенным. Остерегайтесь аргументов filter/where, передающих сырые объекты операторов в запрос Mongo/ORM.

Ошибки и раскрытие информации

Многословные ошибки GraphQL раскрывают стек-трейсы, сообщения драйвера БД и внутренние пути полей. Маскируйте ошибки в проде, логируйте детали на сервере и возвращайте клиентам общие сообщения. Вместе с отключённой интроспекцией это лишает атакующего цикла разведки.

Практический чек-лист укрепления

  • Отключите интроспекцию и IDE в проде; отключите подсказки полей.
  • Применяйте лимиты глубины и сложности и пагинации до выполнения.
  • Ограничьте батчинг и алиасы; rate-limiting по стоимости, а не по числу запросов.
  • Авторизация на уровне объекта в каждом резолвере, отказ по умолчанию, централизованная политика.
  • Параметризованные запросы и строгая валидация ввода в резолверах.
  • Маскируйте ошибки и рассмотрите persisted/allow-listed queries для сильнейшей позиции.

GraphQL не является небезопасным по своей природе, но переносит границу безопасности с эндпоинта в граф резолверов. Смоделируйте этот граф, тестируйте каждый достижимый путь на авторизацию и стоимость и относитесь к схеме как к живой поверхности атаки, а не к документации. При оценке API GraphQL мы сопоставляем находки с CVSS и OWASP API Security Top 10, чтобы приоритизировать устранение по реальному бизнес-влиянию.

graphqlapi-securityowasp-apidosbolaauthorization