跳转到内容
搜索文档

通过 API 配置 GraphQL 恶意查询保护

最后更新 查看 MarkdownAgent 设置

使用 GraphQL API 配置 API 的查询大小和深度限制。 使用 Cloudflare GraphQL API 收集有关您的 GraphQL API 当前使用情况的数据,并配置 Cloudflare 的 GraphQL 恶意查询保护以记录或阻止恶意查询。

简介

查询大小被定义为查询中叶子字段 (terminal fields/leaves) 的数量,而查询深度是叶子所在的最深级别。例如,此查询的大小将被报告为 4 (terminalField[1-4] 均对该计数有贡献),深度将被报告为 3 (terminalField3 和 terminalField4 位于第 3 层深度级别)

GraphQL 查询graphql
{
	terminalField1
	nonTerminalField1(filter: 123) {
		terminalField2
		nonTerminalField2 {
			terminalField3
			terminalField4
		}
	}
}

收集 GraphQL 统计信息

使用 Cloudflare GraphQL API 中新的 apiGatewayGraphqlQueryAnalyticsGroups 节点,您可以检索 apiGatewayGraphqlQuerySizeapiGatewayGraphqlQueryDepth 维度。

GraphQL 查询graphql
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
				}
			}
		}
	}
}

使用上述查询,您将获得以下响应:

响应json
{
	"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 脚本,在给定上述 GraphQL API 响应输出(作为 JSON 文件)的情况下,它将报告查询大小和深度的 p 级别值:

Python 脚本python
#!/usr/bin/env python3

import json
import numpy as np
import argparse

parser = argparse.ArgumentParser()
parser.add_argument("--response", help="指向包含 apiGatewayGraphqlQueryAnalyticsGroups 节点的 API JSON 响应文件的路径", 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"查询大小第 {int(q * 100)} 个百分位数是 {v}" for q, v in zip(quantiles, np.quantile(query_sizes, quantiles))]))
    print('\n'.join([f"查询深度第 {int(q * 100)} 个百分位数是 {v}" for q, v in zip(quantiles, np.quantile(query_depths, quantiles))]))

使用上述查询,您将获得以下输出:

输出示例json
./calculator.py --response=response.json
查询大小第 99 个百分位数是 11.0
查询大小第 95 个百分位数是 11.0
查询大小第 75 个百分位数是 11.0
查询大小第 50 个百分位数是 6.5
查询深度第 99 个百分位数是 1.0
查询深度第 95 个百分位数是 1.0
查询深度第 75 个百分位数是 1.0
查询深度第 50 个百分位数是 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 是否能够成功解析查询。目前,我们运行尽力而为的解析,这意味着我们可能无法解析某些合法的查询。这意味着当您部署 GraphQL 安全规则时,您必须在自定义规则中使用 and cf.api_gateway.graphql.parsed_successfully 过滤器。

例如,您可以通过 API 或仪表板部署以下规则,以阻止深度嵌套且请求超过 30 个字段的查询。

(cf.api_gateway.graphql.query_size > 30 and cf.api_gateway.graphql.query_depth > 7 and cf.api_gateway.graphql.parsed_successfully)

这篇文档对您有帮助吗?