使用 GraphQL API 配置 API 的查询大小和深度限制。 使用 Cloudflare GraphQL API 收集有关您的 GraphQL API 当前使用情况的数据,并配置 Cloudflare 的 GraphQL 恶意查询保护以记录或阻止恶意查询。
查询大小被定义为查询中叶子字段 (terminal fields/leaves) 的数量,而查询深度是叶子所在的最深级别。例如,此查询的大小将被报告为 4 (terminalField[1-4] 均对该计数有贡献),深度将被报告为 3 (terminalField3 和 terminalField4 位于第 3 层深度级别)。
{
terminalField1
nonTerminalField1(filter: 123) {
terminalField2
nonTerminalField2 {
terminalField3
terminalField4
}
}
}使用 Cloudflare GraphQL API 中新的 apiGatewayGraphqlQueryAnalyticsGroups 节点,您可以检索 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 的请求。
您可以使用响应来计算各个属性的百分位数,并设置所允许的阈值。例如,对于查询大小或深度,您可以使用类似于 1.5 * p99 的简单启发式方法。
这里有一个简单的 Python 脚本,在给定上述 GraphQL API 响应输出(作为 JSON 文件)的情况下,它将报告查询大小和深度的 p 级别值:
#!/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))]))使用上述查询,您将获得以下输出:
./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.0API 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)