Brute Force Attack in Adonisjs
How Brute Force Attack Manifests in Adonisjs
Brute force attacks against Adonisjs applications typically target authentication endpoints where attackers systematically try username/password combinations. The framework's built-in authentication system, while robust, can become vulnerable when rate limiting isn't properly configured.
Adonisjs's auth system uses the authenticate method in controllers, which by default doesn't implement any rate limiting. An attacker can send hundreds of requests to /login endpoints, cycling through common passwords or using credential stuffing lists. The framework's session management and JWT token generation happen on each failed attempt, consuming server resources.
The vulnerability becomes more severe when combined with Adonisjs's Lucid ORM. Each authentication attempt typically involves a database query to verify credentials. Without rate limiting, this creates a perfect storm: database connections are exhausted while attackers hammer the login endpoint. The findBy or first queries in authentication middleware run on every attempt, and when attackers use multiple threads or distributed systems, the database can become unresponsive.
Another Adonisjs-specific manifestation occurs in API routes that use token-based authentication. When using @adonisjs/auth with API tokens, each failed token validation triggers a database lookup. Attackers can exploit this by sending malformed or expired tokens to endpoints like /api/users/me or /api/orders, causing repeated database queries that degrade performance.
Custom authentication schemes built with Adonisjs's auth providers can also be vulnerable. Developers sometimes create passwordless authentication using email codes or magic links. Without proper rate limiting on the code generation endpoint, attackers can trigger email floods or exhaust rate limits on email service providers, causing legitimate users to be locked out.
The framework's middleware system, while powerful, requires explicit configuration for security. The default middleware stack doesn't include rate limiting, meaning developers must manually add protections. This creates a situation where many Adonisjs applications are deployed without brute force protections, relying solely on the inherent difficulty of guessing credentials.
Adonisjs-Specific Detection
Detecting brute force vulnerabilities in Adonisjs applications requires examining both the code structure and runtime behavior. Start by auditing your authentication controllers and middleware for rate limiting implementation.
In your login controller, look for the absence of rate limiting middleware. A vulnerable Adonisjs login route might look like:
Route.post('login', 'UserController.login')The secure version includes rate limiting:
Route.post('login', 'UserController.login').middleware('throttle:login,5')Scan your auth configuration files. In config/auth.js, check if you're using session or JWT authentication without additional security layers. The default configuration doesn't include brute force protections.
Examine your route files (start/routes.js) for authentication endpoints. Look for patterns like:
Route.group(() => {
Route.post('login', 'AuthController.login')
Route.post('register', 'AuthController.register')
Route.get('profile', 'UserController.profile').middleware('auth')
}).middleware('guest')Without rate limiting middleware applied to the login and register routes, these endpoints are vulnerable.
Use middleBrick to scan your Adonisjs API endpoints. The scanner tests authentication endpoints by sending multiple failed login attempts and measuring response times and error patterns. It identifies when endpoints accept unlimited authentication attempts without throttling.
middleBrick's LLM security checks are particularly relevant for Adonisjs applications using AI features. If your Adonisjs app includes chatbot functionality or AI-powered features, middleBrick tests for system prompt leakage and prompt injection vulnerabilities that could be exploited alongside brute force attacks.
The scanner also checks for proper error handling. Vulnerable Adonisjs applications often return detailed error messages that help attackers, such as distinguishing between "invalid password" and "user not found" responses. middleBrick flags these information disclosure issues that complement brute force vulnerabilities.
For comprehensive detection, middleBrick analyzes your OpenAPI/Swagger specifications if provided. It cross-references documented authentication flows with actual endpoint behavior, identifying discrepancies where the documentation suggests security measures that aren't implemented in code.
Adonisjs-Specific Remediation
Remediating brute force vulnerabilities in Adonisjs requires implementing rate limiting at the framework level. Adonisjs provides built-in middleware and packages specifically designed for this purpose.
Install the throttle middleware:
npm install @adonisjs/throttleConfigure it in start/kernel.js:
const globalMiddleware = [
'Adonis/Middleware/Cors',
'Adonis/Middleware/Throttle',
'Adonis/Middleware/Static',
'Adonis/Middleware/AuthInit'
]Apply rate limiting to authentication routes in start/routes.js:
Route.post('login', 'AuthController.login').middleware('throttle:login,5,1')
Route.post('register', 'AuthController.register').middleware('throttle:register,3,1')The format is throttle:key,maxAttempts,timer where timer is in minutes. This allows 5 login attempts per minute per IP address.
For API token authentication, implement rate limiting at the route group level:
Route.group(() => {
Route.get('users', 'UserController.index')
Route.get('orders', 'OrderController.index')
}).middleware('auth:api').middleware('throttle:api,100,1')This allows 100 authenticated API requests per minute, which is typically sufficient for legitimate users while blocking automated attacks.
Enhance your authentication controller with account lockout logic:
class AuthController {
async login({ auth, request, response, session }) {
const { email, password } = request.all()
try {
const user = await User.findBy('email', email)
return response.badRequest({ error: 'Invalid credentials' })
}
await auth.attempt(email, password)
return response.ok({ message: 'Login successful' })
} catch (error) {
if (error.name === 'InvalidJwtToken') {
return response.unauthorized({ error: 'Invalid credentials' })
}
// Track failed attempts in database
await this.trackFailedAttempt(email)
}
}
async trackFailedAttempt(email) {
const user = await User.findBy('email', email)
if (user) {
user.failed_login_attempts++
user.last_failed_attempt = new Date()
if (user.failed_login_attempts >= 5) {
user.locked_until = new Date(Date.now() + 15 * 60000) // 15 minutes
}
await user.save()
}
}
}This tracks failed attempts and locks accounts after 5 failures for 15 minutes.
For distributed applications, use Redis for rate limiting to maintain consistency across multiple server instances:
const Redis = use('Redis')
const MAX_ATTEMPTS = 5
const LOCKOUT_DURATION = 15 * 60 // 15 minutes
async checkBruteForce(email) {
const key = `brute-force:${email}`
const attempts = await Redis.get(key)
if (attempts && parseInt(attempts) >= MAX_ATTEMPTS) {
return true // Account locked
}
await Redis.incr(key)
await Redis.expire(key, LOCKOUT_DURATION)
return false
}Implement CAPTCHA for additional protection after multiple failed attempts:
async login({ request, response, session }) {
const { email, password, captcha } = request.all()
if (await this.isLockedOut(email)) {
return response.tooManyRequests({ error: 'Too many attempts' })
}
if (await this.shouldShowCaptcha(email)) {
if (!this.verifyCaptcha(captcha)) {
return response.badRequest({ error: 'Invalid captcha' })
}
}
// ... authentication logic
}Finally, implement comprehensive logging to detect brute force patterns:
const Logger = use('Logger')
async logAuthAttempt(email, success, ip) {
email,
success,
ip,
timestamp: new Date(),
userAgent: request.header('User-Agent')
}
Logger.info('auth_attempt', logData)
// Send to monitoring service
await this.sendToMonitoring(logData)
}