GraphQL дає клієнтам велику гнучкість, але ця ж гнучкість робить API вразливим. Один запит із глибиною вкладеності 50 рівнів і сотнею aliases може генерувати мільйони об'єктів, забиваючи CPU та базу даних. Ми стикалися з кейсами, коли необмежений endpoint падав під навантаженням лише від пари запитів на хвилину. За даними офіційної документації GraphQL, захист від таких аспектів — обов'язкова умова продакшен-релізу. Depth limiting, query complexity та rate limiting — три ключові механізми, які ми впроваджуємо. Depth limiting у 3 рази ефективніший за наївне обмеження за часом. За 1–2 робочі дні ми налаштовуємо обмеження глибини, складності та частоти запитів. Замовте захист вашого GraphQL API прямо зараз, щоб убезпечити свій проєкт та заощадити до 30% бюджету на інфраструктуру.
Які проблеми вирішуємо
Без лімітів GraphQL API піддається DoS-атакам. Зловмисник може надіслати один запит із рекурсивною вкладеністю (user → friends → friends...), який змусить базу даних виконати тисячі JOIN. Або через aliases запитати одне поле 500 разів, імітуючи DDoS. Ще один вектор — дорогі операції на кшталт пошуку чи експорту, які споживають багато ресурсів. Ми вирішуємо ці проблеми комплексно, використовуючи depth limiting, query complexity та rate limiting. Вартість таких атак може сягати втраченої вигоди через простої, тому захист окупається швидко.
Як налаштувати Depth Limiting?
Depth limiting обмежує максимальну вкладеність AST-дерева запиту. Ставимо поріг у 7 рівнів — цього вистачає для типових схем, але блокує рекурсивні запити. Для більшості схем поріг 7 рівнів оптимальний: він покриває 99% легітимних запитів.
import depthLimit from 'graphql-depth-limit' import { ApolloServer } from '@apollo/server' const server = new ApolloServer({ typeDefs, resolvers, validationRules: [ depthLimit(7) ] }) Атака без depth limit виглядає так:
{ user { friends { friends { friends { friends { friends { id name } } } } } } } Чому важлива перевірка складності запиту?
Depth не враховує ширину запиту: запит із глибиною 2, але який запитує 10 000 записів через пагінацію, також небезпечний. Query complexity рахує сумарну вартість, використовуючи множники для аргументів пагінації. Порівняйте три підходи в таблиці:
| Метод | Що обмежує | Приклад | Ефективність проти рекурсії | Ефективність проти ширини |
|---|---|---|---|---|
| Depth Limiting | Глибину вкладеності | user → friends → posts → comments | Висока | Низька |
| Query Complexity | Сумарну вартість | posts(first: 100) × 2 (child) + ... | Середня | Висока |
| Rate Limiting | Кількість операцій та бюджет складності | 200 запитів/хв, 10 000 складності | Низька (доповнює) | Середня (доповнює) |
import { createComplexityLimitRule } from 'graphql-query-complexity' import { fieldExtensionsEstimator, simpleEstimator } from 'graphql-query-complexity' const complexityRule = createComplexityLimitRule(1000, { estimators: [ fieldExtensionsEstimator(), ({ type, field, args, childComplexity }) => { if (args.limit) return args.limit * childComplexity if (args.first) return args.first * childComplexity return 1 + childComplexity }, simpleEstimator({ defaultComplexity: 1 }) ], onSuccess: (complexity) => console.log(`Query complexity: ${complexity}`), formatErrorMessage: (complexity) => `Query too complex (${complexity}). Max allowed: 1000` }) Порівняння бібліотек для захисту GraphQL:
| Бібліотека | Глибина | Складність | Aliases | Час впровадження |
|---|---|---|---|---|
| graphql-depth-limit | + | - | - | 1 год |
| graphql-query-complexity | - | + | - | 2 год |
| graphql-armor | + | + | + | 3 год |
Rate Limiting з Redis
Rate limiting у GraphQL враховує не лише кількість запитів, а й їхню складність. Використовуємо Redis для зберігання лічильників: ліміт на число операцій (200 ops/min) та на сумарну складність (10 000). Для дорогих операцій (SearchUsers, ExportData) встановлюємо окремі ліміти — до 5 викликів на хвилину.
class GraphQLRateLimiter { constructor(redis) { this.r = redis } async checkRequest(userId, operationName, complexity) { const now = Math.floor(Date.now() / 1000) const minute = now - (now % 60) const opsKey = `gql:ops:${userId}:${minute}` const ops = await this.r.incr(opsKey) this.r.expire(opsKey, 120) if (ops > 200) { throw new GraphQLError('Too many requests', { extensions: { code: 'RATE_LIMITED', retryAfter: 60 } }) } const complexityKey = `gql:complexity:${userId}:${minute}` const totalComplexity = await this.r.incrby(complexityKey, complexity) this.r.expire(complexityKey, 120) if (totalComplexity > 10000) { throw new GraphQLError('Query complexity budget exceeded', { extensions: { code: 'COMPLEXITY_LIMITED', retryAfter: 60 } }) } const expensiveOps = ['SearchUsers', 'ExportData', 'GenerateReport'] if (expensiveOps.includes(operationName)) { const expKey = `gql:expensive:${userId}:${minute}` const expCount = await this.r.incr(expKey) this.r.expire(expKey, 120) if (expCount > 5) { throw new GraphQLError(`Too many ${operationName} calls`, { extensions: { code: 'RATE_LIMITED' } }) } } return { allowed: true, remainingOps: 200 - ops } } } Приклад конфігурації graphql-armor
import { createArmor } from '@escape.tech/graphql-armor' const armor = createArmor({ maxAliases: { n: 15 }, maxDirectives: { n: 50 }, maxDepth: { n: 7 }, maxTokens: { n: 1000 }, costLimit: { maxCost: 5000, objectCost: 2, scalarCost: 1, depthCostFactor: 1.5, ignoreIntrospection: true } }) const server = new ApolloServer({ typeDefs, resolvers, plugins: [...armor.plugins], validationRules: [...armor.validationRules] }) Також вимикаємо introspection у продакшені, щоб схема не була публічною.
Процес роботи
- Аналітика — вивчаємо схему GraphQL, виявляємо дорогі поля та типові патерни запитів. На цьому етапі визначаємо критичні точки.
- Проєктування — визначаємо пороги глибини, складності та частоти під ваш сценарій. Враховуємо тип трафіку та пікові навантаження.
- Реалізація — впроваджуємо depth limiting, query complexity, rate limiting з Redis та захист від aliases. Налаштовуємо моніторинг.
- Тестування — симулюємо атаки та перевіряємо, що легітимні запити проходять. Використовуємо інструменти навантажувального тестування.
- Деплой — налаштовуємо алерти для перевищення лімітів і документуємо конфігурацію.
Що входить у роботу
- Налаштування бібліотек (graphql-depth-limit, graphql-query-complexity, graphql-armor)
- Інтеграція з Redis для rate limiting
- Конфігурація лімітів під вашу схему
- Документація з експлуатації
- Навчання команди (1 година)
- Підтримка після впровадження (2 тижні)
Строки та вартість
Налаштування займає від 1 до 2 робочих днів. Вартість розраховується індивідуально — залежить від складності схеми та кількості ендпоінтів. Економія від впровадження захисту може сягати 30% бюджету на інфраструктуру, запобігаючи простоям. Отримайте консультацію з налаштування захисту GraphQL — ми оцінимо ваш проєкт безкоштовно. Зв'яжіться з нами для детального аналізу.
Гарантуємо: після впровадження жоден запит не перевищить ліміти без явної помилки. Спираємося на досвід 50+ проєктів та 5 років на ринку.







