← Cloudflare API Shield / api-shield / security / graphql-protection
Настройка защиты от вредоносных запросов GraphQL через API
Используйте Cloudflare GraphQL API чтобы собрать данные о текущем использовании вашего GraphQL API и настроить защиту GraphQL от вредоносных запросов Cloudflare для их логирования или блокировки.
Введение
Размер запроса определяется как количество терминальных полей (листьев) в запросе, а глубина запроса означает самый глубокий уровень, на котором присутствует лист. Например, размер этого запроса будет указан как 4 (terminalField[1-4] all contribute to this counter), а глубина будет указана как 3 (terminalField3 and terminalField4 are at depth level 3).
{
terminalField1
nonTerminalField1(filter: 123) {
terminalField2
nonTerminalField2 {
terminalField3
terminalField4
}
}
}Сбор статистики GraphQL
Используя новый apiGatewayGraphqlQueryAnalyticsGroups узел в GraphQL API Cloudflare, вы можете получить apiGatewayGraphqlQuerySize и apiGatewayGraphqlQueryDepth измерений.
query ApiGatewayGraphqlQueryAnalytics(
$zoneTag: string
$start: Time
$end: Time
) {
viewer {
zones(filter: { zoneTag: $zoneTag }) {
apiGatewayGraphqlQueryAnalyticsGroups(
limit: 100
orderBy: [
apiGatewayGraphqlQuerySize_DESC
apiGatewayGraphqlQueryDepth_DESC
]
filter: { datetime_geq: $start, datetime_leq: $end }
) {
count
dimensions {
apiGatewayGraphqlQuerySize
apiGatewayGraphqlQueryDepth
}
}
}
}
}При выполнении запроса выше вы получите следующий ответ:
{
"data": {
"viewer": {
"zones": [
{
"apiGatewayGraphqlQueryAnalyticsGroups": [
{
"count": 10,
"dimensions": {
"apiGatewayGraphqlQueryDepth": 1,
"apiGatewayGraphqlQuerySize": 11
}
},
{
"count": 10,
"dimensions": {
"apiGatewayGraphqlQueryDepth": 1,
"apiGatewayGraphqlQuerySize": 2
}
}
]
}
]
}
},
"errors": null
}В примере ответа Cloudflare зафиксировал 10 запросов с глубиной 1 и размером 11, а также 10 запросов с глубиной 1 и размером 2 за выбранный период времени.
Анализ статистики GraphQL
Вы можете использовать ответ для расчета процентилей по атрибутам и задать порог допустимых значений. Например, можно применить простую эвристику вроде 1.5 * p99 для размера или глубины запроса.
Ниже приведён простой скрипт Python, который выводит размер запроса и уровни глубины p-level на основе приведённого выше вывода ответа API GraphQL (в виде JSON-файла):
#!/usr/bin/env python3
import json
import numpy as np
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("--response", help="Path to the API JSON response file with the apiGatewayGraphqlQueryAnalyticsGroups node", required=True)
args = parser.parse_args()
with open(args.response) as f:
query_sizes = np.array([], dtype=np.uint16)
query_depths = np.array([], dtype=np.uint8)
data = json.load(f)['data']['viewer']['zones'][0]['apiGatewayGraphqlQueryAnalyticsGroups']
for datapoint in data:
query_sizes = np.append(query_sizes, [datapoint['dimensions']['apiGatewayGraphqlQuerySize']] * datapoint['count'])
query_depths = np.append(query_depths, [datapoint['dimensions']['apiGatewayGraphqlQueryDepth']] * datapoint['count'])
quantiles = [0.99, 0.95, 0.75, 0.5]
print('\n'.join([f"Query size {int(q * 100)}th percentile is {v}" for q, v in zip(quantiles, np.quantile(query_sizes, quantiles))]))
print('\n'.join([f"Query depth {int(q * 100)}th percentile is {v}" for q, v in zip(quantiles, np.quantile(query_depths, quantiles))]))При выполнении запроса выше вы получите следующий вывод:
./calculator.py --response=response.json
Query size 99th percentile is 11.0
Query size 95th percentile is 11.0
Query size 75th percentile is 11.0
Query size 50th percentile is 6.5
Query depth 99th percentile is 1.0
Query depth 95th percentile is 1.0
Query depth 75th percentile is 1.0
Query depth 50th percentile is 1.0Ограничение входящих запросов GraphQL
Теперь клиентам API Shield доступны три новых поля в пользовательских правилах:
cf.api_gateway.graphql.query_sizeописывает размер запроса GraphQL.cf.api_gateway.graphql.query_depthописывает глубину запроса GraphQL.cf.api_gateway.graphql.parsed_successfullyуказывает, удалось ли Cloudflare разобрать запрос. В настоящее время используется разбор по принципу best effort, то есть некоторые корректные запросы могут быть не разобраны. Это означает, что вам необходимо использоватьand cf.api_gateway.graphql.parsed_successfullyфильтр в пользовательских правилах при развёртывании правил безопасности GraphQL.
Например, с помощью API или панели управления можно развернуть следующее правило, которое блокирует запросы с глубокой вложенностью и обращением более чем к 30 полям.
(cf.api_gateway.graphql.query_size > 30 and cf.api_gateway.graphql.query_depth > 7 and cf.api_gateway.graphql.parsed_successfully)