Соответствие ограничениям GraphQL¶
Вы можете настроить ПроAPI Защита для проверки входящих запросов GraphQL на соответствие предопределенным ограничениям запросов. Придерживаясь этих ограничений, вы можете защитить свой GraphQL API от вредоносных запросов, включая потенциальные DoS-атаки. В этом руководстве объясняется, как ПроAPI Защита вычисляет атрибуты запроса, такие как запросы узлов, глубину запроса и сложность, прежде чем согласовывать их с заданными вами параметрами.
При запуске Docker-контейнера ПроAPI Защита для API GraphQL вы устанавливаете ограничения, используя следующие переменные среды:
Переменная среды | Описание |
---|---|
APIFW_GRAPHQL_MAX_QUERY_COMPLEXITY | Определяет максимальное количество запросов узла, которое может потребоваться для выполнения запроса. Установка значения «0» отключает проверку сложности. |
APIFW_GRAPHQL_MAX_QUERY_DEPTH | Указывает максимально допустимую глубину запроса GraphQL. Значение «0» означает, что проверка глубины запроса пропускается. |
APIFW_GRAPHQL_NODE_COUNT_LIMIT | Устанавливает верхний предел количества узлов в запросе. Если установлено значение «0», проверка ограничения количества узлов пропускается. |
APIFW_GRAPHQL_MAX_ALIASES_NUM | Устанавливает ограничение на количество псевдонимов, которые можно использовать в документе GraphQL. Если для этой переменной установлено значение «0», это означает, что количество используемых псевдонимов не ограничено. |
APIFW_GRAPHQL_FIELD_DUPLICATION | Определяет, разрешить или запретить дублирование полей в документе GraphQL. Значение по умолчанию — false (предотвратить). |
APIFW_GRAPHQL_BATCH_QUERY_LIMIT | Устанавливает ограничение на количество запросов, которые можно объединить в один запрос GraphQL. Если для этой переменной установлено значение «0», это означает, что количество пакетных запросов не ограничено. |
Как работает расчет лимита¶
ПроAPI Защита использует библиотеку wundergraph/graphql-go-tools, которая использует алгоритмы, аналогичные тем, которые используются GitHub для расчета сложности запросов GraphQL. Центральное место в этом занимает функция OperationComplexityEstimator
, которая обрабатывает определение схемы и запрос, итеративно исследуя запрос, чтобы определить его сложность и глубину.
Вы можете уточнить этот расчет, интегрируя целочисленные аргументы в поля, которые обозначают количество узлов, возвращаемых полем:
-
directive @nodeCountMultiply on ARGUMENT_DEFINITION
Указывает, что значение Int, к которому применяется директива, должно использоваться в качестве множителя Ноды.
-
directive @nodeCountSkip on FIELD
Указывает, что алгоритм должна пропускать эта Нода. Это полезно для внесения в белый список определенных путей запроса, например, для самоанализа.
Для документов с несколькими запросами вычисленная сложность, глубина и количество узлов применяются ко всему документу, а не только к одному выполняемому запросу.
Примеры расчета¶
Ниже приведены несколько примеров, которые дадут более четкое представление о расчетах. Они основаны на следующей схеме GraphQL
type User {
name: String!
messages(first: Int! @nodeCountMultiply): [Message]
}
type Message {
id: ID!
text: String!
createdBy: String!
createdAt: Time!
}
type Query {
__schema: __Schema! @nodeCountSkip
users(first: Int! @nodeCountMultiply): [User]
messages(first: Int! @nodeCountMultiply): [Message]
}
type Mutation {
post(text: String!, username: String!, roomName: String!): Message!
}
type Subscription {
messageAdded(roomName: String!): Message!
}
scalar Time
directive @nodeCountMultiply on ARGUMENT_DEFINITION
directive @nodeCountSkip on FIELD
Глубина всегда представляет уровни вложенности полей. Например, запрос ниже имеет глубину 3:
{
a {
b {
c
}
}
}
Пример 1¶
query {
users(first: 10) {
name
messages(first:100) {
id
text
}
}
}
-
NodeCount = {int} 1010
Node count = 10 [users(first: 10)] + 10*100 [messages(first:100)] = 1010
-
Complexity = {int} 11
Complexity = 1 [users(first: 10)] + 10 [messages(first:100)] = 11
-
Depth = {int} 3
Пример 2¶
query {
users(first: 10) {
name
}
}
-
NodeCount = {int} 10
Node count = 10 [users(first: 10)] = 10
-
Complexity = {int} 1
Complexity = 1 [users(first: 10)] = 1
-
Depth = {int} 2
Пример 3¶
query {
message(id:1) {
id
text
}
}
-
NodeCount = {int} 1
Node count = 1 [message(fid:1)] = 1
-
Complexity = {int} 1
Complexity = 1 [messages(first:1)] = 1
-
Depth = {int} 2
Пример 4¶
query {
users(first: 10) {
name
messages(first:1) {
id
text
}
}
}
-
NodeCount = {int} 20
Node count = 10 [users(first: 10)] + 10*1 [messages(first:1)] = 20
-
Complexity = {int} 11
Complexity = 1 [users(first: 10)] + 10 [messages(first:1)] = 11
-
Depth = {int} 3
Пример 5 (интроспекционный запрос)¶
query IntrospectionQuery {
__schema {
queryType {
name
}
mutationType {
name
}
subscriptionType {
name
}
types {
...FullType
}
directives {
name
description
locations
args {
...InputValue
}
}
}
}
fragment FullType on __Type {
kind
name
description
fields(includeDeprecated: true) {
name
description
args {
...InputValue
}
type {
...TypeRef
}
isDeprecated
deprecationReason
}
inputFields {
...InputValue
}
interfaces {
...TypeRef
}
enumValues(includeDeprecated: true) {
name
description
isDeprecated
deprecationReason
}
possibleTypes {
...TypeRef
}
}
fragment InputValue on __InputValue {
name
description
type {
...TypeRef
}
defaultValue
}
fragment TypeRef on __Type {
kind
name
ofType {
kind
name
ofType {
kind
name
ofType {
kind
name
ofType {
kind
name
ofType {
kind
name
ofType {
kind
name
ofType {
kind
name
}
}
}
}
}
}
}
}
-
NodeCount = {int} 0
-
Complexity = {int} 0
-
Depth = {int} 0
Поскольку в схеме присутствует директива __schema: __Schema! @nodeCountSkip
вычисленные NodeCount, Complexity и Depth равны 0.
Пример 6 (ограничение пакетных запросов)¶
Предположим, вы установили для переменной среды APIFW_GRAPHQL_BATCH_QUERY_LIMIT
значение 2
. Если вы попытаетесь последовательно выполнить следующий пакет из 3 запросов GraphQL к бэкэнду:
[
{"query":"query {\n systemHealth\n}","variables":[]},
{"query":"query {\n systemHealth\n}","variables":[]},
{"query":"query {\n systemHealth\n}","variables":[]}
]
ПроAPI Защита перехватит этот запрос и зарегистрирует ошибку, указывающую, что количество запросов в пакете превышает настроенный лимит. Зарегистрированное сообщение об ошибке будет иметь вид:
ERROR GraphQL query validation error=the batch query limit has been exceeded. The number of queries in the batch is 3. The current batch query limit is 2 protocol=HTTP