Rate Limiting Bypass in Adonisjs with Basic Auth
Rate Limiting Bypass in Adonisjs with Basic Auth — how this specific combination creates or exposes the vulnerability
AdonisJS applications that rely solely on HTTP Basic Auth for authentication can expose a Rate Limiting Bypass when rate limiting is configured only at the framework or middleware layer without considering the authentication mechanism. Basic Auth credentials are typically sent on every request in the Authorization header. If rate limiting is applied before authentication is fully established or if the limiter is keyed only by IP address, an attacker can rotate credentials to evade limits without triggering detection.
In AdonisJS, the built-in rate limiter (often implemented via the @adonisjs/throttle package) can be configured to limit requests per IP or by a custom key. When Basic Auth is in use, the optimal key is a combination of the authenticated user identifier and IP, rather than IP alone. If the limiter key ignores the authenticated user, an attacker with a pool of valid credentials can distribute requests across many accounts, staying under per-IP thresholds and bypassing intended protection.
Another bypass scenario arises when preflight or OPTIONS requests are not subjected to the same rate limits as authenticated actions. AdonisJS routes often include public endpoints for CORS preflight that do not enforce authentication. If the rate limiter does not apply uniformly to these routes, an attacker can use OPTIONS requests to consume limited quota without authenticating, indirectly enabling abuse of authenticated endpoints.
Additionally, if the application uses route-based grouping with shared middleware, and the rate limiter is attached at a higher level without scoping to the auth guard, the limiter might not fire for certain controller methods. This misconfiguration allows authenticated requests that should be rate limited to escape enforcement when they traverse routes that lack explicit limits.
For an attacker, this combination means they can repeatedly call sensitive endpoints (such as password reset or token generation) by cycling through stolen Basic Auth credentials, each with a distinct username but a shared or low per-IP cap. The vulnerability maps to OWASP API Top 10 API4:2023 — Injection and API1:2023 — Broken Object Level Authorization when abused to exhaust resources or trigger account lockouts.
middleBrick detects this class of issue by correlating unauthenticated scan behavior with OpenAPI spec analysis and runtime probing. It checks whether rate limiting is evaluated in the context of authenticated identities and flags configurations where the limiter key is IP-only when authentication is present. Findings include severity, a description of the bypass path, and remediation guidance that aligns with compliance frameworks such as OWASP API Top 10 and SOC2 controls.
Basic Auth-Specific Remediation in Adonisjs — concrete code fixes
To prevent Rate Limiting Bypass in AdonisJS when using Basic Auth, tie the rate limiter key to the authenticated user identity and ensure limits apply consistently across all relevant routes. Below are concrete, syntactically correct examples.
1. Configure the throttle key to include the authenticated user
Use the username claim from the Basic Auth identity as part of the rate limiter key. This ensures each user has an independent quota, preventing credential rotation attacks.
import Route from '@ioc:Adonis/Core/Route'
import Throttle from '@ioc:Adonis/Addons/Throttle'
Route.group(() => {
Route.get('/account', async ({ auth }) => {
const user = await auth.authenticate()
// Business logic
}).throttle({ key: `user:${user.id}`, limit: 60, window: '1m' })
}).prefix('api')
2. Apply rate limits at the route level with authenticated guards
Ensure routes that accept Basic Auth explicitly use the auth guard and that the limiter references the authenticated user. Avoid relying on IP-only keys when credentials are used.
import Route from '@ioc:Adonis/Core/Route'
Route.get('/profile', async ({ auth }) => {
const user = await auth.use('basic').authenticate()
return { user: user.username }
}).throttle({
// key combines guard type and user identifier
key: `basic:${auth.user?.id}`,
limit: 30,
window: '1m',
})
3. Extend throttle middleware to include Basic Auth credentials
Customize the throttle key resolver to incorporate the Basic Auth header when a user is authenticated, ensuring the limiter cannot be bypassed by rotating credentials.
import { ThrottleResolverContract } from '@ioc:Adonis/Addons/Throttle'
const customResolver: ThrottleResolverContract = async ({ auth, request }) => {
if (auth.isAuthenticated) {
const user = auth.user
return `user:${user.id}`
}
// Fallback to IP when unauthenticated, but enforce strict limits
return `ip:${request.ip()}`
}
// In start/throttle.ts or provider registration
import Throttle from '@ioc:Adonis/Addons/Throttle'
Throttle.resolver = customResolver
4. Apply consistent limits to CORS preflight and authenticated routes
Include OPTIONS and CORS routes under the same rate limiting policies to prevent quota harvesting via unauthenticated methods.
Route.options('*', async () => {
return {}
}).throttle({ key: `ip:${ctx.request.ip()}`, limit: 10, window: '1m' })
5. Example Basic Auth handler with explicit guard usage
Ensure your login or auth routes explicitly declare the Basic Auth guard and are appropriately limited to prevent enumeration abuse.
import Route from '@ioc:Adonis/Core/Route'
import { schema } from '@ioc:Adonis/Core/Validator'
Route.post('/login', async ({ request, auth, response }) => {
const body = request.validate({
schema: schema.create({
username: schema.string(),
password: schema.string.visibility('secret'),
}),
})
try {
const user = await auth.use('basic').authenticate()
return response.ok(user)
} catch {
return response.unauthorized()
}
}).throttle({ key: 'login_attempts_ip', limit: 5, window: '5m' })
By aligning the throttle key with the authenticated user and enforcing limits uniformly, you mitigate credential-rotation bypasses and ensure that rate limiting remains effective in an environment where HTTP Basic Auth is used.
Related CWEs: resourceConsumption
| CWE ID | Name | Severity |
|---|---|---|
| CWE-400 | Uncontrolled Resource Consumption | HIGH |
| CWE-770 | Allocation of Resources Without Limits | MEDIUM |
| CWE-799 | Improper Control of Interaction Frequency | MEDIUM |
| CWE-835 | Infinite Loop | HIGH |
| CWE-1050 | Excessive Platform Resource Consumption | MEDIUM |