GraphQL Circular References
GraphQL 循环引用
描述
GraphQL 允许客户端请求特定数据,可以利用其灵活性来创建复杂或递归查询。当对象类型直接或间接通过其他类型引用回自身时,就会发生循环引用。
例如:
query CircularReferences {
user {
friends {
user {
friends {
user {
__typename
}
}
}
}
}
}
GraphQL 中循环引用的安全影响:
- 拒绝服务:通过发送包含太多嵌套引用的大型查询,攻击者可能会使服务器不堪重负,导致其运行缓慢或崩溃。
- 资源耗尽:服务器在处理查询时可能会耗尽内存或 CPU 资源,从而导致性能下降或服务不可用。
建议
为了降低 GraphQL 中发生循环引用的风险,您可以遵循以下建议: 1. 深度限制: 实现一个中间件来检查查询的深度,并在超过限制时抛出错误。 示例:
class DepthAnalysisMiddleware:
def resolve(self, next, root, info, **args):
if info.operation.selection_set:
depth = 0
for field in info.operation.selection_set.selections:
depth = max(depth, self._get_depth(field))
if depth > 3:
raise Exception('Query depth is too high')
return next(root, info, **args)
def _get_depth(self, field):
if field.selection_set:
return 1 + max(self._get_depth(f) for f in field.selection_set.selections)
return 1
- 循环引用检测: 重新设计模式以避免循环引用。 循环引用示例:
class User(graphene.ObjectType):
id = graphene.ID()
name = graphene.String()
friends = graphene.List(lambda: User)
def resolve_friends(self, info):
return [User(id=1, name='Alice'), User(id=2, name='Bob')]
重新设计的模式示例:
class FriendProfile(graphene.ObjectType):
id = graphene.ID()
name = graphene.String()
class User(graphene.ObjectType):
id = graphene.ID()
name = graphene.String()
friends = graphene.List(FriendProfile)
def resolve_friends(self, info):
return [FriendProfile(id=1, name='Alice'), FriendProfile(id=2, name='Bob')]
const FriendProfile = new GraphQLObjectType({
name: 'FriendProfile',
fields: {
id: { type: GraphQLID },
name: { type: GraphQLString }
}
});
const User = new GraphQLObjectType({
name: 'User',
fields: {
id: { type: GraphQLID },
name: { type: GraphQLString },
friends: { type: new GraphQLList(FriendProfile) }
}
});
链接
标准
- CWE_TOP_25:
- CWE_400
- PCI_STANDARDS:
- REQ_6_2
- REQ_6_4
- REQ_11_3
- OWASP_MASVS_L2:
- MSTG_PLATFORM_2
- OWASP_ASVS_L3:
- V13_4_1
- SOC2_CONTROLS:
- CC_2_1
- CC_4_1
- CC_7_1
- CC_7_2
- CC_7_4
- CC_7_5
- CC_9_1
- HIPAA_CONTROLS:
- SECURITY212
- SECURITY213