GraphQL Array-based Query Batching Allowed: Potential Batching Attack Vulnerability
Description
The GraphQL API accepts array-based query batching, allowing 10 or more queries to be executed in a single HTTP request. While query batching is a legitimate performance optimization technique that reduces network overhead, it can be abused by attackers to bypass rate limiting controls and amplify the effectiveness of automated attacks. This configuration creates a security risk when batching is enabled without proper safeguards in production environments.
Remediation
Implement the following controls to mitigate GraphQL batching attacks:
1. Limit Batch Size: Restrict the maximum number of queries allowed in a single batched request to a reasonable threshold (e.g., 3-5 queries). Configure this limit at the GraphQL server level.
2. Apply Rate Limiting: Implement rate limiting that accounts for batched queries by counting each individual query within a batch toward the rate limit, not just the HTTP request itself.
3. Disable Batching for Sensitive Operations: Explicitly disable query batching for authentication endpoints, password reset functionality, and other security-critical operations.
4. Implement Query Complexity Analysis: Use query cost analysis to assign complexity scores to operations and reject batched requests that exceed defined thresholds.
Example configuration for limiting batch size in Apollo Server:
const server = new ApolloServer({
typeDefs,
resolvers,
plugins: [
{
async requestDidStart() {
return {
async didResolveOperation(requestContext) {
const batchSize = Array.isArray(requestContext.request.query)
? requestContext.request.query.length
: 1;
if (batchSize > 5) {
throw new Error('Batch size exceeds maximum allowed limit of 5');
}
}
};
}
}
]
});
Example using express-graphql with custom validation:
app.use('/graphql', (req, res, next) => {
if (Array.isArray(req.body)) {
if (req.body.length > 5) {
return res.status(400).json({
error: 'Batch queries limited to 5 operations'
});
}
}
next();
}, graphqlHTTP({ schema, graphiql: false }));