Array-Based Batch Queries
Array-Based Batch Queries
Description
Array-based batch queries in GraphQL occur when an attacker sends multiple queries in a single request using an array structure, which can overwhelm the server's processing capabilities.
In GraphQL, clients are typically allowed to send a single query per request. However, if not properly restricted, attackers can bundle multiple queries into a single request using an array. This can result in a Denial of Service (DoS) attack by consuming excessive server resources, degrading performance, or causing a service outage.
Example:
[
{ query: "{ user(id: 1) { name } }" },
{ query: "{ user(id: 2) { name } }" },
{ query: "{ user(id: 3) { name } }" }
]
Security Impact of Array-Based Batch Queries:
Denial of Service: Attackers can send large batches of queries in a single request, forcing the server to handle numerous operations simultaneously. This can lead to resource exhaustion and potential service crashes. Resource Exhaustion: Like Field Duplication, array-based batch queries can spike CPU and memory usage by overloading the server with a large number of queries in a single call. Service Disruption: If left unchecked, array-based batch queries can make the GraphQL API unavailable to legitimate users, causing system downtime or degraded performance.
Recommendation
To mitigate the risk of array-based batch queries attacks in GraphQL, where multiple queries are bundled into a single request, you can apply the following strategies:
- Implement Query Complexity Limits: Set rules that account for the number of queries being processed in a single request. This helps limit the load on the server and prevents attackers from sending excessively large batches of queries.
- Limit the Number of Queries Per Request: Enforce server-side restrictions on how many queries can be sent in a single batch. Tools like GraphQL Armor can help impose limits and prevent resource overloading through array-based batch queries.
// Configuring for GraphQL Armor
GraphQLArmorConfig({
maxBatchQueries: {
// Enable or disable the plugin | default: true
enabled: true,
// Set the maximum number of queries allowed per request | default: 5
n: 5,
// Callbacks to execute when a query batch is accepted
onAccept: [],
// Callbacks to execute when a query batch is rejected
onReject: [],
// Propagate rejection details to the client | default: true
propagateOnRejection: true,
}
});
import graphql
from graphql.language import ast
from graphql.language import parser
from settings import api
def validate_batch_queries(query: str) -> None:
"""
This validation prevents the execution of requests containing excessive
batch queries to avoid overloading the server.
"""
class BatchQueryParser(parser.Parser):
def parse_batch_queries(self) -> list[ast.OperationDefinitionNode]:
query_count = 0
while self.peek(graphql.TokenKind.BRACKET_L):
self.parse_operation_definition()
query_count += 1
if query_count > api.API_MAX_BATCH_QUERIES:
raise graphql.GraphQLError("Exception - Max batch queries exceeded")
return []
ast_parser = BatchQueryParser(query)
ast_parser.parse_document()
Links
Standards
- CWE_TOP_25:
- CWE_20
- PCI_STANDARDS:
- REQ_6_2
- REQ_6_4
- REQ_11_3
- OWASP_MASVS_L2:
- MSTG_PLATFORM_2
- OWASP_ASVS_L2:
- V5_3_3
- V5_3_4
- V9_1_1
- V12_1_1
- V12_1_2
- OWASP_ASVS_L3:
- V5_3_3
- V5_3_4
- V12_1_1
- V12_1_2
- V12_1_3
- SOC2_CONTROLS:
- CC_2_1
- CC_4_1
- CC_7_1
- CC_7_2
- CC_7_4
- CC_7_5
- CC_9_1