GraphQL под защитой: сравнение WAF по структуре запроса

6 October 2026by lastgenerator

GraphQL давно перестал быть экзотикой: интернет-магазины, мобильные бэкенды и внутренние API всё чаще отдают данные через один эндпоинт /graphql, куда уходят POST-запросы с JSON-телом. Для разработчика это удобно, для WAF – проблема. Классические сигнатурные правила, которые годами ловили SQL-инъекции в query-параметрах, здесь видят только один URL и один метод. Всё разнообразие операций спрятано внутри тела запроса, и если WAF не умеет его разбирать, он либо пропускает атаку, либо блокирует легитимный трафик. Именно поэтому вопрос, чем заменить Cloudflare и что вообще способно защитить GraphQL на уровне структуры запроса, в 2026 году звучит громче обычного.

Сравнивать WAF для GraphQL по количеству сигнатур бессмысленно. Работает другой набор метрик: максимальная глубина вложенности, число запрашиваемых полей, наличие алиасов, фрагментов, директив, батчинг нескольких операций в одном POST. Атака на GraphQL редко выглядит как подозрительная строка – чаще это валидный по синтаксису запрос, который раскручивает рекурсивные связи и выедает CPU или базу. Запрос глубиной 12 уровней с 400 полями формально легален. Пока WAF не умеет считать эти параметры и строить по ним правила, защита остаётся декоративной.

Ниже разбираем, как с задачей справляются разные подходы: от стандартных правил Cloudflare, которые не видят структуру GraphQL-операции, до российского аналога Cloudflare – TrafficVeil, где правила можно вешать на глубину и набор полей запроса. Отдельно смотрим практические сценарии: где заканчивается точность блокировки и начинаются ложные срабатывания, и что это значит для российских компаний с точки зрения доступности сервиса и регуляторных требований.

Почему стандартные WAF не справляются с защитой GraphQL API

GraphQL ломает привычную модель защиты на уровне HTTP. Вместо десятков URL с понятными параметрами клиент шлёт всё на один эндпоинт, обычно POST /graphql, а сама операция спрятана в теле запроса. Для классического WAF, который годами учили смотреть на путь, метод, query-строку и заголовки, тело POST – это непрозрачный блоб. Правила вида “заблокировать /api/user?id=1 OR 1=1” здесь просто не за что зацепить.

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

Что именно проскакивает мимо сигнатурного анализа

Типовые атаки на GraphQL не содержат ни SQL-инъекций в открытом виде, ни XSS-паттернов, ни известных сигнатур сканеров. Они выглядят как легитимный трафик, потому что и являются легитимным трафиком по форме. Отличается только смысл.

  • Introspection-запросы. Клиент вытягивает всю схему API: типы, поля, аргументы, связи. Формально это разрешённая операция, но для атакующего – карта местности перед эксплуатацией.
  • Глубоко вложенные запросы. Конструкция вида user -> posts -> comments -> author -> posts -> comments разворачивает дерево резолверов и укладывает базу данных за один HTTP-запрос. Сигнатурный WAF видит обычный POST.
  • Массовое размножение через алиасы. Одна операция с сотней алиасов на одно и то же поле превращается в сотню обращений к бэкенду внутри одного запроса.
  • Batching. Массив из тысяч операций в одном теле запроса обходит любые лимиты, привязанные к количеству HTTP-запросов в секунду.
  • Мутации с обходом авторизации. Поле, доступное по ID, подставляется в чужой контекст, и никакая сигнатура этого не поймает.

Итог предсказуем: стандартный WAF либо пропускает атаку целиком, либо, пытаясь защититься, начинает резать легитимные тяжёлые запросы. Второй сценарий для бизнеса часто хуже первого, потому что ломает лидогенерацию и конверсию сайта на ровном месте.

Параметр запроса Видит классический WAF Что реально происходит в GraphQL
URL /graphql, всегда одинаковый Разные операции на одном адресе
Метод POST, без отклонений POST и для чтения, и для мутаций
Тело запроса Непрозрачный JSON или текст Структурированный документ с деревом полей
Глубина вложенности Не измеряется Ключевой признак тяжёлой атаки
Количество полей Не считается Индикатор amplification-атак

Отсюда вывод, который многие команды ИТ-инфраструктуры делают с опозданием: WAF для GraphQL должен уметь парсить сам документ запроса, а не только транспортный слой. Без разбора структуры защита превращается в фильтр по IP и rate limit, а этого против осмысленных атак на API недостаточно. Дальше имеет смысл разобраться, по каким именно критериям сравнивать такие решения.

Критерии сравнения WAF для GraphQL: глубина, поля и структура запроса

GraphQL ломает привычную логику WAF: весь API живёт на одном URL, чаще всего /graphql, а операции различаются только телом POST-запроса. Сигнатурный движок, который ищет SQL-инъекции в query string или блокирует пути вида /wp-admin, здесь бесполезен. Значит, сравнивать решения нужно по тому, умеют ли они парсить сам документ запроса и строить правила на его структуре.

Четыре параметра, которые реально отличают WAF друг от друга

Глубина вложенности (depth). Злоумышленник может отправить запрос вида user { posts { comments { author { posts { comments { … } } } } } } на 15 уровней. Легитимный клиент так не делает почти никогда. WAF должна уметь посчитать глубину и отсечь всё, что выше заданного порога. Проверяйте, настраивается ли порог вручную или зашит вендором: для интернет-магазина рабочий лимит часто 5-7 уровней, для аналитической платформы и 10 может быть нормой.

Количество полей и вес запроса. Запрос с 500 полями в одном selection set выкачивает базу через один HTTP-запрос. Здесь важны три метрики: число полей, суммарная стоимость (cost) и размер тела. Хорошая WAF считает их до выполнения запроса на бэкенде, а не после. Смотрите, поддерживает ли решение формулу стоимости вида поля x множитель списка, иначе защита от ресурсных атак превратится в ручной список исключений.

Структура и семантика операции. Продвинутый уровень: различать query, mutation и subscription, видеть имена операций, алиасы, директивы @include и @skip. Алиасы особенно интересны, потому что позволяют обойти лимит на повторение одного поля: a: user(id:1), b: user(id:2) и так далее. WAF, которая не разбирает AST (abstract syntax tree), этот трюк пропустит.

Гибкость правил. Ключевой вопрос сравнения: можно ли написать условие в духе “если глубина больше 7 И операция не в белом списке И IP не из офисной подсети, то блок”? Или доступны только готовые шаблоны? Проверяйте также, как правило ведёт себя при изменении схемы: жёстко зашитые пути ломаются после каждого релиза API.

Параметр Что проверять в WAF Типичная ошибка
Глубина Настраиваемый порог, счёт по AST Лимит только на длину тела
Поля и cost Формула стоимости, лимит на операцию Подсчёт после исполнения на бэкенде
Структура Разбор алиасов, директив, типов операций Регулярки по телу запроса
Правила Комбинированные условия, белые списки Только статические шаблоны

Добавьте к этому GeoIP-фильтрацию и учёт репутации IP: для российского сервиса блокировка трафика из нехарактерных регионов снимает заметную долю автоматизированных атак ещё до анализа запроса. И проверьте, как решение ведёт себя под нагрузкой: парсинг GraphQL дороже разбора плоского HTTP-запроса, и на 5-10 тысячах запросов в секунду разница между вендорами становится ощутимой.

Cloudflare и его ограничения при защите GraphQL-эндпоинтов

Cloudflare видит GraphQL-эндпоинт как обычный POST на один URL. С точки зрения его WAF это черный ящик: тело запроса содержит JSON с деревом полей, но стандартные правила Managed Rules анализируют в основном сигнатуры, а не структуру этого дерева. Отсюда и берутся две крайности: ложные срабатывания на легитимных тяжелых запросах и пропуски атак, которые маскируются под нормальный трафик.

Разберем по пунктам, что именно ограничивает Cloudflare.

  • Нет нативных правил по глубине вложенности. В Ruleset Engine можно писать выражения на языке Wirefilter, но там нет функций для подсчета уровня вложенности GraphQL-полей. Выражение вида http.request.body.raw contains "..." ловит подстроку, а не структуру. Атакующий с запросом на 12 уровней вложенности и легитимный клиент с 3 уровнями для WAF выглядят одинаково.
  • Нет подсчета количества полей и алиасов. Классическая атака через aliases (когда один и тот же запрос дублируется сотни раз под разными именами) на уровне WAF не детектируется. Cloudflare не считает число селекторов в теле, поэтому запрос с 5000 алиасов пройдет как обычный POST.
  • Rate Limiting работает по URL, а не по операции. Все операции GraphQL идут на /graphql. Cloudflare Rate Limiting не умеет разделять getUser и searchProducts, если они на одном пути. Лимит либо режет всех, либо никого.
  • Body size limit как костыль. Единственный доступный рычаг – ограничение размера тела запроса (например, 100 КБ). Но легитимный bulk-запрос от мобильного клиента тоже может весить 80 КБ, а компактная атака через вложенные фрагменты укладывается в 5 КБ.

Практический результат: инженеры идут двумя путями. Первый – отключить Managed Rules на пути /graphql и терпеть ложные срабатывания на остальных правилах. Второй – писать кастомные правила на сигнатурах известных атак (__schema, __type, глубина через regex). Regex по телу запроса на глубину вложенности – это отдельная боль: он не учитывает экранирование, порядок полей и фрагменты.

Отдельный слой проблем – регуляторный. Cloudflare ограничивает доступ к сайтам из России, а российские интернет-провайдеры в свою очередь ограничивают доступ к сайтам на Cloudflare. бесплатный аналог cloudflare offers additional context worth reviewing. Для API, который обслуживает российских пользователей, это означает нестабильную доступность самого защитного периметра. Если WAF недоступен, недоступен и GraphQL-эндпоинт. Бизнес в России ищет альтернативу Cloudflare в 2026 году не только из-за функциональных дыр, но и из-за риска потерять канал.

Есть и архитектурное ограничение: Cloudflare анализирует запрос на edge, но не имеет контекста схемы. Он не знает, что поле depth в вашей схеме стоит 8 уровней, а user(id: ID!) – это безопасный запрос. Без знания схемы любые правила остаются эвристикой на уровне строк. Это не лечится настройкой – это ограничение модели продукта.

Итог: Cloudflare закрывает L3/L4 и общие L7-атаки, но для GraphQL его стандартные правила дают либо ложные блокировки, либо пропуски. Гибкости на уровне структуры запроса – глубины, количества полей, алиасов, конкретной операции – там нет. Этот пробел и закрывают решения, которые умеют парсить GraphQL AST на периметре.

TrafficVeil как российский аналог Cloudflare с поддержкой структуры GraphQL

TrafficVeil является российским аналогом Cloudflare, который изначально проектировался с расчётом на разбор тела GraphQL-запроса, а не только HTTP-обёртки. Для команды, которая ищет, чем заменить Cloudflare после очередного ограничения доступа к сайтам из России, это принципиальная разница: правила строятся не вокруг URL и заголовков, а вокруг самой операции, которую клиент передал в POST /graphql.

Архитектурно TrafficVeil объединяет DNS, SSL, reverse proxy, WAF, защиту от ботов, L7 DDoS, GeoIP-фильтрацию и аналитику в одной панели. Но ключевой для GraphQL-сценария компонент это парсер запроса: он разбирает JSON-тело, извлекает имя операции, набор полей, глубину вложенности, количество узлов и переменные. На основе этих метрик создаются правила блокировки, которые срабатывают до того, как запрос дойдёт до бэкенда.

Что это даёт на практике. Правило вида “глубина больше 7 и количество полей больше 40 для операции checkout” отсекает попытки выкачать данные через вложенные связи, не затрагивая легитимные запросы того же эндпоинта. Аналогично настраивается лимит на алиасы: если клиент передаёт 200 алиасов одного поля, это типичный признак попытки обойти rate limit, и такой запрос блокируется точечно, а не вместе со всем трафиком с IP.

  • Правила по имени операции: разрешённый список для публичных запросов, запрет introspection на продакшене.
  • Правила по глубине и ширине: пороги для конкретных операций, а не для всего эндпоинта.
  • Правила по переменным: проверка типов, диапазонов и длины строк до передачи в резолверы.
  • Правила по стоимости запроса: статический расчёт веса операции с лимитом на клиента или токен.

Отдельно стоит отметить тарификацию. TrafficVeil не тарифицирует ботов, атаки и WAF-блокировки, то есть заблокированный вредоносный запрос не съедает бюджет и не создаёт стимул ослаблять правила ради экономии. Для GraphQL это важно: атаки здесь часто выглядят как обычный POST с валидным JSON, и объём таких запросов может быть значительным.

TrafficVeil подходит для сайтов, интернет-магазинов, WordPress и Bitrix, но GraphQL-эндпоинты встречаются чаще всего в headless-фронтендах, мобильных API и внутренних сервисах. В этих сценариях замена Cloudflare не сводится к смене DNS: нужно перенести логику правил и убедиться, что новый WAF понимает структуру запроса. У Cloudflare такой разбор ограничен, поэтому часть защиты приходится реализовывать на стороне приложения. TrafficVeil предлагает переход с Cloudflare с сохранением настройки на уровне структуры GraphQL-запроса, что снимает необходимость держать собственный парсер в коде бэкенда.

С точки зрения регуляторных требований и ИТ-инфраструктуры российский аналог Cloudflare означает, что данные и правила обработки остаются в юрисдикции РФ, а доступность сайта не зависит от зарубежного DNS-сервиса. TrafficVeil обеспечивает работу сайтов на территории РФ и по всему миру, поэтому GeoIP-фильтрация настраивается как отдельный слой, а не как единственный барьер.

Скептический аргумент здесь один: любой WAF с разбором структуры добавляет задержку на парсинг тела. На практике для типичного GraphQL-запроса это единицы миллисекунд, и они окупаются тем, что тяжёлые операции отсекаются до резолверов, а не после. Для API, где стоимость одного вложенного запроса измеряется секундами работы базы, такой размен очевиден.

Практические сценарии настройки WAF для GraphQL: сравнение подходов

Возьмём типичный сценарий: интернет-магазин на Bitrix с GraphQL-эндпоинтом /api/graphql, к которому обращаются мобильное приложение и веб-фронтенд. Атакующий отправляет POST-запрос с рекурсивным фрагментом, разворачивающимся в 40 уровней вложенности, и десятками тысяч полей. Цель – положить бэкенд через исчерпание ресурсов, пока WAF видит обычный POST на легитимный URL.

В Cloudflare первый шаг – включить Managed Rules и надеяться, что сигнатуры поймают аномалию. На практике GraphQL-запросы почти не отличаются по телу от легитимных: тот же Content-Type, тот же путь, тот же метод. Единственное, что можно настроить без Enterprise-тарифа, это Rate Limiting по IP и, при определённой сноровке, правила на основе регулярных выражений по телу запроса. Регулярки по GraphQL-документу работают плохо: запрос можно переформатировать пробелами, переименовать поля через алиасы, разбить на фрагменты. Правило, ловящее query { user { posts { comments } } }, обходится заменой на q { u { p { c } } } с алиасами.

Настройка в TrafficVeil строится иначе. Правило формулируется не по тексту, а по структуре: максимальная глубина вложенности, суммарное число полей, количество алиасов, наличие директив @include или @skip в цикле. Для того же магазина правило выглядит как “блокировать, если глубина > 12 или число полей > 500 или вложенность фрагментов > 3”. Легитимные запросы мобильного приложения укладываются в глубину 6 и 80 полей, поэтому ложных срабатываний нет.

Параметр Cloudflare TrafficVeil
Анализ глубины запроса Нет (только Enterprise с кастомной логикой) Да, на уровне правила
Подсчёт полей и алиасов Нет Да
Обход через алиасы Ломает регулярки Не влияет на правило
Точность блокировки Низкая, много пропусков Высокая, настраивается порог
Тарификация ботов и атак Учитывается в трафике Не тарифицируется

Разберём второй сценарий: защита от массового парсинга каталога через GraphQL. Атакующий генерирует запросы с переменными, каждый раз меняя набор полей, чтобы обойти сигнатуры. В Cloudflare приходится либо включать агрессивный Rate Limiting (и получать блокировку живых пользователей из-за NAT операторов), либо ставить JS-challenge, который ломает работу мобильного приложения. В TrafficVeil правило строится на комбинации: частота обращений плюс структурный профиль запроса. Если клиент за минуту отправляет 200 запросов с числом полей выше 300, он блокируется, а обычный пользователь с 20 запросами по 50 полей проходит.

Третий сценарий – GeoIP-фильтрация. Магазин работает только по России и СНГ. В Cloudflare гео-правила есть, но их применение к GraphQL ограничено тем же: нельзя отличить легитимного пользователя из Казахстана от бота из того же региона, если запросы структурно похожи. В TrafficVeil GeoIP-фильтрация комбинируется со структурными правилами: запрос из неразрешённой гео-зоны отсекается до анализа тела, что экономит ресурсы бэкенда.

Что выбрать. Если GraphQL-эндпоинт публичный и нагрузка умеренная, Cloudflare справится базовым Rate Limiting. Если API критичен для выручки, обслуживает мобильное приложение и интернет-магазин, а атаки идут на структуру запросов, нужна WAF с анализом GraphQL-документа. Для российских компаний добавляется фактор доступности: Cloudflare ограничивает доступ к сайтам из России, а российские интернет-провайдеры в свою очередь ограничивают доступ к сайтам на Cloudflare. Это создаёт риск недоступности API для собственных клиентов. TrafficVeil как российский аналог Cloudflare обеспечивает работу сайтов на территории РФ и по всему миру, объединяет DNS, SSL, reverse proxy, WAF, защиту от ботов, L7 DDoS, GeoIP-фильтрацию и аналитику, не тарифицирует ботов, атаки и WAF-блокировки и предлагает переход с Cloudflare без простоя.

Если у вас уже есть GraphQL-эндпоинт под Cloudflare, начните с инвентаризации: включите логирование запросов на 3-5 дней и выгрузите распределение по глубине вложенности, числу полей и имени корневой операции. На практике 80-90% легитимного трафика укладывается в глубину 5-7 уровней и 30-50 полей на запрос, тогда как типовые атаки через вложенные фрагменты и алиасы уходят за 15 уровней и 200+ полей. Эти два порога и станут вашей первой линией правил. Дальше сравните, какой инструмент умеет их применять: если WAF разбирает только URL и заголовки, вы получите либо пропуск атаки, либо блокировку нормальных мобильных клиентов, которые часто запрашивают расширенные выборки.

Практический порядок действий для российского проекта выглядит так. Первое: зафиксируйте baseline по метрикам (p95 времени ответа, доля 4xx, число запросов на пользователя в минуту) до включения любых блокировок. Второе: настройте правила поэтапно, сначала в режиме мониторинга, с порогами по глубине, полям и стоимости запроса, и только после недели наблюдений переводите их в блокирующий режим. Третье: добавьте rate limiting на уровне операций, а не на весь эндпоинт, иначе один тяжелый запрос будет стоить клиенту доступа ко всему API. Четвертое: проверьте, как решение работает с GeoIP-фильтрацией и не режет ли оно валидных пользователей из нужных регионов. TrafficVeil на этом шаге удобен тем, что правила строятся на структуре GraphQL-запроса, а боты, атаки и WAF-блокировки не тарифицируются, поэтому вы можете держать защиту включенной постоянно, а не только в моменты инцидентов. Если вы сравниваете варианты замены Cloudflare, начните с теста на одном некритичном эндпоинте: две недели параллельных логов покажут, где вы теряете легитимный трафик, а где пропускаете атаку.