Источник: https://github.com/AnaktaCTF/CTF/blob/main — WEB/GraphQL.md
GraphQL – это язык запросов API, который позволяет клиентам гибко запрашивать только нужные данные. В отличие от REST API, GraphQL позволяет объединять несколько запросов в один, что делает его мощным инструментом. Однако его гибкость и универсальность создают серьезные проблемы безопасности, если API не настроен должным образом. В этой статье разобраны основные векторы атак на GraphQL, методы защиты и инструменты тестирования безопасности.
Архитектура GraphQL и отличие от REST
GraphQL и REST – два подхода к построению API, но они отличаются принципами работы.

REST (Representational State Transfer) строится на основе множества эндпоинтов, каждый из которых отвечает за определенный ресурс (например, /players, /teams, /matches). Клиент делает несколько запросов к разным эндпоинтам, чтобы получить нужные данные. Это может привести к:
-
избыточной передаче данных (
over-fetching), когда клиент получает больше информации, чем необходимо; -
недостаточной передаче данных (
under-fetching), когда клиенту приходится делать несколько запросов, чтобы собрать всю необходимую информацию.
GraphQL использует единый эндпоинт (/graphql), который принимает запросы на получение только необходимых данных. Клиент может запрашивать конкретные поля, избегая проблем over-fetching и under-fetching. Однако такой подход создает новые риски:
-
возможность утечки скрытых данных, если схема API не настроена должным образом;
-
уязвимость к сложным рекурсивным запросам, которые могут перегрузить сервер.
Основные концепции GraphQL
Схема: определяет типы данных, доступные в API, и отношения между ними. Схема служит контрактом между клиентом и сервером, описывая, какие запросы можно выполнять и какие данные можно получать.
Типы: основные строительные блоки схемы. Включают скалярные типы (например, Int, String, Boolean) и объектные типы, которые могут содержать несколько полей различных типов.
Запросы: используются для чтения данных. Клиент определяет структуру запроса, и сервер возвращает данные в точно таком же формате.
Мутации: используются для изменения данных (создание, обновление, удаление).
Подписки: позволяют клиенту получать обновления в реальном времени при изменении данных на сервере.
Пример схемы GraphQL:
type Author {
id: ID!
name: String!
books: [Book]
}
type Book {
id: ID!
title: String!
author: Author
}
type Query {
authors: [Author]
books: [Book]
book(id: ID!): Book
}
type Mutation {
addBook(title: String!, authorId: ID!): Book
}
В данном примере определяются типы Author и Book, а также запросы для получения списка авторов, списка книг и конкретной книги по ID. Мутация addBook позволяет добавить новую книгу с указанным названием и идентификатором автора.
Основные уязвимости в GraphQL
-
Инспекция схемы (Introspection Attack)
GraphQL поддерживает встроенную возможность introspection (самоанализ), которая позволяет клиенту запрашивать структуру API, включая все доступные типы, запросы и мутации. Хоть это и удобно, но может стать уязвимостью, если не отключено в продакшене. Злоумышленник, получивший доступ к схеме, может использовать её для поиска скрытых API и уязвимых эндпоинтов.
Запрос introspection в GraphQL Playground:
{ __schema { types { name fields { name } } } }Если introspection включен, сервер вернет структуру API, позволяя атакующему анализировать потенциальные цели.
-
Инъекции в GraphQL (GraphQL Injection)
GraphQL-инъекции похожи на традиционные SQL-инъекции. Они происходят, когда входные данные пользователя обрабатываются без проверки и используются для выполнения внутренних запросов.
Пример GraphQL-инъекции:
{ user(id: "1 OR 1=1") { username } }Если API уязвимо, оно может вернуть список всех пользователей вместо одного. Что может привести к утечке конфиденциальной информации.
Другой вариант атаки – внедрение в мутации:
mutation { updateUser(id: "1", username: "hacker") { username } }Если сервер не проверяет права доступа, атакующий может изменить данные других пользователей.
-
DDoS-атаки через сложные запросы
GraphQL позволяет создавать вложенные запросы, что может быть использовано злоумышленниками для перегрузки сервера. Если сервер не ограничивает глубину рекурсивных запросов, злоумышленник может создать бесконечную рекурсию и вызвать отказ в обслуживании.
Пример атаки:
{ user { friends { friends { friends { username } } } } }Если сервер не настроен на ограничение глубины вложенных запросов, он будет обрабатывать их до полного исчерпания ресурсов.
Методы и способы защиты GraphQL API
-
Ограничение доступа к запросам интроспекции: интроспекция позволяет клиентам получать информацию о схеме API. В производственной среде рекомендуется отключать интроспекцию, чтобы злоумышленники не могли получить информацию о структуре API.
-
Валидация входных данных: необходимо проверять и ограничивать входные данные, чтобы предотвратить инъекции и другие атаки. Например, можно использовать директивы для валидации данных на уровне схемы.
-
Ограничение глубины и сложности запросов: чтобы предотвратить атаки типа "отказ в обслуживании" (DoS) с помощью глубоких или сложных рекурсивных запросов, следует устанавливать ограничения на максимальную глубину и сложность запросов.
-
Ограничение частоты запросов (Rate Limiting): для предотвращения перебора уязвимых мутаций или брутфорс-атак рекомендуется ограничивать количество запросов от одного клиента за определенный период времени. Это можно реализовать с помощью специальных плагинов или middleware.
-
Использование безопасного транспортного протокола: при использовании HTTP для запросов и мутаций необходимо применять HTTPS для шифрования данных и защиты их от перехвата.
-
Аутентификация и авторизация: необходимо внедрять надежные механизмы аутентификации и авторизации, чтобы гарантировать, что только авторизованные пользователи могут выполнять определенные операции или получать доступ к определенным данным.
Пример реализации ограничения глубины запросов на Node.js с использованием graphql-depth-limit:
const express = require('express');
const { graphqlHTTP } = require('express-graphql');
const { makeExecutableSchema } = require('@graphql-tools/schema');
const depthLimit = require('graphql-depth-limit');
const typeDefs = `
type Query {
hello: String
}
`;
const resolvers = {
Query: {
hello: () => 'Hello world!',
},
};
const schema = makeExecutableSchema({ typeDefs, resolvers });
const app = express();
app.use(
'/graphql',
graphqlHTTP({
schema: schema,
validationRules: [depthLimit(5)], // Ограничение глубины запросов до 5
})
);
app.listen(4000, () => {
console.log('Server is running on http://localhost:4000/graphql');
});
В данном примере используется middleware graphql-depth-limit для ограничения глубины запросов до 5 уровней, что помогает предотвратить атаки с использованием чрезмерно глубоких запросов.
Реализация перечисленных мер безопасности позволит защитить GraphQL API от распространенных уязвимостей и обеспечить стабильную и безопасную работу сервиса.
Инструменты для тестирования безопасности GraphQL
GraphQLmap – автоматизированный инструмент для тестирования GraphQL API на уязвимости.

GraphQL Voyager – инструмент для визуализации схем API и поиска потенциальных уязвимостей.

InQL (Burp Suite Plugin) – расширение Burp Suite для анализа GraphQL API и обнаружения скрытых эндпоинтов.

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