Rate Limit Bypass in Adonisjs
Rate Limit Bypass in AdonisJS
AdonisJS applications typically enforce rate limits using middleware such as Throttle or RateLimiter. When these mechanisms are misconfigured or bypassed, attackers can flood endpoints with requests, leading to denial of service or credential brute force. A common pattern involves using route definitions that lack proper throttling or that rely on client-supplied headers without validation. For example, the following route incorrectly applies throttling only when a specific header is present, allowing an attacker to omit that header and bypass the limit:
// routes.js
Route.get('/api/user', 'UserController.index')
.middleware('throttle', { limit: 5, expire: 60 })
.params({ requiresAuth: true });
// In the controller
const User = use('App/Models/User');
class UserController {
async index({ request }) {
// No explicit rate limit check here
return await User.all();
}
}
In this configuration, the throttle middleware is attached but only enforces a limit of 5 requests per 60 seconds when the request includes the expected header. If the header is missing or altered, the middleware silently skips enforcement, allowing unlimited access. Attackers can exploit this by sending requests without the header or by spoofing it, thereby bypassing the intended rate limit. Additionally, the middleware may be applied after the controller method executes, meaning the business logic runs before any throttling check, further increasing exposure.
Another manifestation occurs when developers use custom middleware that reads the limit from query parameters or request bodies, enabling attackers to manipulate the limit value dynamically. For instance:
// Custom middleware
const rateLimiter = (ctx, next) => {
const limit = parseInt(ctx.request.input('limit')) || 10;
const expire = parseInt(ctx.request.input('expire')) || 60;
// Missing validation of limit and expire values
if (ctx.request.ip() in allowedIps) {
// No enforcement here
}
return next();
};
Route.get('/api/data', 'DataController.show')
.middleware('rateLimiter');
Such designs allow attackers to set the limit to a high value or to bypass checks entirely, resulting in a rate limit bypass that defeats the purpose of protection. These patterns are especially dangerous when combined with public APIs that do not require authentication, as they expose endpoints to unlimited traffic from anywhere on the internet.
AdonisJS-Specific Detection
middleBrick detects rate limit bypasses by scanning the unauthenticated attack surface of AdonisJS endpoints. The scanner parses the OpenAPI/Swagger specification associated with each route and cross-references it with runtime behavior to identify missing or ineffective throttling. For example, if a route is documented with a 429 Too Many Requests response but the underlying controller does not raise this status or does not enforce a limit, middleBrick flags the discrepancy as a potential bypass. The scanner also monitors HTTP headers such as X-RateLimit-Limit and X-RateLimit-Remaining for consistency across requests, detecting when these headers are absent or manipulated.
During a scan, middleBrick sends a series of requests that omit the expected authentication header or alter the throttling header, then observes whether the application responds with success or with a throttling error. If the endpoint continues to return 200 OK under high request volume without rate limiting, the scanner classifies this as a rate limit bypass. The findings include a severity rating, a description of the vulnerable endpoint, and a remediation recommendation tailored to AdonisJS.
AdonisJS-Specific Remediation
To remediate rate limit bypasses in AdonisJS, ensure that throttling middleware is applied before any controller logic executes and that the configuration does not depend on mutable request parameters. Use the built-in Throttle middleware with a fixed limit and expiration that cannot be overridden by client input. For example:
// Start by defining a reusable throttle config in a separate file
const Throttle = require('@adonisjs/core/middleware/throttle')
// In start/kernel.js
const Route = use('Route')
Route.group(() => {
Route.get('/api/user', 'UserController.index')
.middleware('throttle', {
maxRequests: 10,
perMinutes: 1,
keyGenerator: (request) => {
// Use IP address for per-client throttling
return request.ip()
}
})
.prefix('api')
.middleware('auth') // Ensure authentication is required
})();
// In the controller, do not reapply throttling or modify the limit
class UserController {
async index({ response }) {
const users = await User.all()
return response.json(users)
}
}
Additionally, validate any custom middleware that reads limit values from request data. Reject non-numeric or out-of-range inputs and enforce a server-side default. Example:
const rateLimiter = (ctx, next) => {
const limit = parseInt(ctx.request.input('limit'));
const expire = parseInt(ctx.request.input('expire'));
if (isNaN(limit) || limit < 1 || limit > 100 || isNaN(expire) || expire < 1) {
return response.status(400).json({ error: 'Invalid rate limit parameters' });
}
// Apply a fixed limit regardless of input
return next();
};
Finally, enable logging of throttling events to monitor for abnormal request patterns. Use AdonisJS's built-in logger to record when the 429 response is generated, and set up alerts if the rate limit is exceeded frequently. This approach ensures that the rate limit remains enforceable and that any bypass attempts are quickly detectable.
Frequently Asked Questions
What specific AdonisJS middleware is commonly misconfigured leading to rate limit bypasses?
Throttle middleware is commonly misconfigured when it is applied after controller logic or when its limit and expiration values are derived from request parameters without validation, allowing attackers to bypass rate limiting.How can middleBrick help identify a rate limit bypass in an AdonisJS application?
429 error. It flags discrepancies between documented behavior and actual runtime responses, providing severity ratings and remediation steps.