Graphql Batching in Adonisjs
Adonisjs-Specific Detection
Detecting GraphQL batching vulnerabilities in Adonisjs requires analyzing both the endpoint configuration and runtime behavior. middleBrick identifies this issue during its unauthenticated black-box scan by sending batched GraphQL requests and measuring response patterns. Specifically, it checks whether the endpoint accepts an array of operations and processes them without enforcing limits on operation count, depth, or cost.
During a scan, middleBrick sends a payload like:
[
{ "query": "query { user(id: 1) { id name } }" },
{ "query": "query { user(id: 2) { id name } }" },
... // up to 50 operations
]
If the server returns a successful array of responses (e.g., HTTP 200 with an array of result objects) and the response time scales linearly with the number of operations — without hitting a 429 or 400 error — middleBrick flags this as a potential batching amplification risk. It correlates this with other findings: if batched queries trigger IDOR (BOLA) or expose excessive data, the severity increases.
Additionally, middleBrick reviews the Adonisjs route definition (if OpenAPI/Swagger spec is available) to detect whether the /graphql endpoint is configured to accept array payloads. It cross-references spec definitions with runtime behavior to confirm if the API explicitly allows batching without constraints.
Adonisjs-Specific Remediation
To mitigate GraphQL batching attacks in Adonisjs, implement operation limits and validation at the route level. Since @adonisjs/graphql-server does not provide built-in batching controls, you must wrap the GraphQL execution logic with custom validation.
Update your route controller to reject batched requests or enforce strict limits. Here’s a secure implementation using Adonisjs conventions:
import Route from '@ioc:Adonis/Core/Route'
import { graphql } from 'graphql'
import { schema } from 'App/GraphQL/schema'
Route.post('/graphql', async ({ request, response }) => {
const body = request.all()
// Reject batched requests (array of operations)
if (Array.isArray(body)) {
return response.badRequest({ error: 'Batched GraphQL operations are not allowed' })
}
const { query, variables, operationName } = body
// Optional: Limit query depth or cost using external libraries
// Example: const depthLimit = createDepthLimit(5)
// const validatedQuery = depthLimit(query)
try {
const result = await graphql({
schema,
source: query,
variableValues: variables,
operationName,
// validationRules: [depthLimit] // if using depth limit
})
return response.send(result)
} catch (error) {
return response.badRequest({ error: error.message })
}
}).as('graphql')
Alternatively, if batching is required for legitimate use cases, implement an operation counter with a low threshold (e.g., max 2 operations):
const MAX_BATCH_SIZE = 2
if (Array.isArray(body) && body.length > MAX_BATCH_SIZE) {
return response.tooManyRequests({
error: `Maximum ${MAX_BATCH_SIZE} operations allowed per request`
})
}
// Process each operation sequentially with individual timeouts
const results = await Promise.all(
body.map(op =>
graphql({ schema, source: op.query, variableValues: op.variables, operationName: op.operationName })
)
)
return response.send(results)
This approach ensures that even if batching is permitted, resource consumption remains bounded. Combine this with Adonisjs’s built-in rate limiting middleware (Route.middleware(['rateLimiter'])) to limit requests per IP, preventing abuse at the network layer.
Frequently Asked Questions
Does Adonisjs have built-in protection against GraphQL batching attacks?
@adonisjs/graphql-server package does not include native operation counting, depth limiting, or batch size restrictions. Developers must implement these controls manually in their route controllers or via custom middleware to prevent amplification attacks.