Broken Authentication in Adonisjs
How Broken Authentication Manifests in Adonisjs
Broken authentication in Adonisjs applications typically occurs through several framework-specific patterns. The most common vulnerability appears in session management where developers fail to properly configure the session middleware or use default settings that aren't suitable for production environments.
A critical Adonisjs-specific issue arises when using the default session driver without proper encryption. Consider this vulnerable configuration:
// start/kernel.js - vulnerable configuration
const namedMiddleware = {
auth: 'Adonis/Middleware/Auth',
session: 'Adonis/Middleware/Session'
}
// Using default session settings without encryption
const session = {
driver: 'cookie',
secret: process.env.SESSION_SECRET || 'default-secret', // <--- CRITICAL VULNERABILITY
httpOnly: true,
maxAge: 24 * 60 * 60 * 1000
}The default 'default-secret' allows session hijacking since the session data is stored in plaintext cookies. An attacker who obtains a user's cookie can immediately impersonate them without needing credentials.
Another Adonisjs-specific pattern involves improper use of the auth middleware. Developers often forget to protect routes or misconfigure authentication guards:
// routes.js - vulnerable route protection
Route.get('/admin/dashboard', ({ auth }) => {
// No guard specified - uses default which might be incorrect
if (auth.user) {
return view.render('admin.dashboard')
}
return 'Unauthorized'
}).middleware('auth')This code appears secure but fails if the default guard isn't properly configured or if multiple guards exist. An attacker can exploit this by crafting requests that bypass authentication checks.
Password handling in Adonisjs applications often reveals broken authentication through weak hashing or missing rate limiting. The framework's built-in Hash provider makes it easy to hash passwords correctly, but developers sometimes bypass it:
// Vulnerable password storage
const User = use('App/Models/User')
class UserController {
async register({ request }) {
const userData = request.only(['username', 'password'])
// CRITICAL: Storing plaintext passwords
const user = await User.create({
username: userData.username,
password: userData.password // Should use Hash.make()
})
return user
}
}This code stores passwords in plaintext, making database breaches catastrophic. Even with the Hash provider available, developers might forget to use it or use insufficient iterations.
Token-based authentication in Adonisjs APIs often suffers from broken authentication when JWT secrets are hardcoded or weak:
// config/auth.js - vulnerable JWT configuration
module.exports = {
authenticator: 'jwt',
secret: 'super-secret-jwt-key', // Hardcoded secret - easily compromised
options: {
expiresIn: '1 days',
algorithm: 'HS256'
}
}Hardcoded secrets mean that if source code is exposed through any means, all tokens become immediately forgeable. This is particularly dangerous in open-source projects or when code is shared between environments.
Session fixation attacks target Adonisjs applications when developers don't regenerate session IDs after authentication. The framework provides auth.attempt() which should handle this, but custom implementations often miss it:
// Vulnerable login implementation
class AuthController {
async login({ auth, request, response }) {
const { email, password } = request.all()
// CRITICAL: No session fixation protection
const user = await User.query().where('email', email).first()
if (user && await Hash.verify(password, user.password)) {
await auth.generate(user) // Doesn't regenerate session ID
return response.redirect('/dashboard')
}
return response.badRequest('Invalid credentials')
}
}This implementation allows session fixation because the session ID remains the same before and after authentication. An attacker can fixate a session ID, trick a victim into authenticating with it, and then use the same session ID to access the account.
Adonisjs-Specific Detection
Detecting broken authentication in Adonisjs requires examining both configuration files and runtime behavior. Start with configuration analysis:
// Check for vulnerable session configuration
const sessionConfig = require('../config/session')
if (sessionConfig.secret === 'default-secret') {
console.warn('CRITICAL: Using default session secret')
}
if (sessionConfig.driver === 'cookie' && !sessionConfig.encryption) {
console.warn('WARNING: Cookie sessions not encrypted')
}Run this check during application startup to catch configuration issues early. For JWT tokens, validate secret strength:
// Check JWT secret strength
const authConfig = require('../config/auth')
const crypto = require('crypto')
function isWeakSecret(secret) {
// Check if secret is default or too short
const weakSecrets = ['super-secret', 'jwt-secret', 'your-secret-here']
if (weakSecrets.includes(secret)) return true
if (secret.length < 32) return true // Too short for HS256
// Check entropy - weak secrets often have low entropy
const hash = crypto.createHash('sha256').update(secret).digest('hex')
const entropy = hash.split('').reduce((sum, char) => {
return sum + char.charCodeAt(0)
}, 0) / hash.length
return entropy < 50 // Arbitrary threshold for weak secrets
}
if (isWeakSecret(authConfig.secret)) {
console.error('CRITICAL: Weak JWT secret detected')
}For runtime detection, implement middleware that checks authentication patterns:
// middleware/auth-check.js
const User = use('App/Models/User')
class AuthCheck {
async handle({ auth, request, response }, next) {
// Check if auth is being used without proper guard specification
if (!auth.authenticator && request.url().startsWith('/api')) {
console.warn(`Missing authenticator on API route: ${request.url()}`)
}
// Check for session fixation vulnerability
const isLogin = request.url() === '/login' && request.method() === 'POST'
if (isLogin) {
const oldSessionId = request.cookie('adonis-session')
await next()
const newSessionId = response.lazyBody._content.cookie['adonis-session']
if (oldSessionId === newSessionId) {
console.error('Session fixation vulnerability: session ID not regenerated')
}
} else {
await next()
}
}
}
module.exports = AuthCheckAutomated scanning with middleBrick provides comprehensive detection without modifying your codebase. The CLI tool scans your running Adonisjs application:
npm install -g middlebrick
# Scan your Adonisjs API
middlebrick scan https://yourapp.com/api --output json
# Output shows authentication-specific findings:
{
"authentication": {
"score": 45,
"severity": "high",
"findings": [
{
"title": "Weak session secret detected",
"severity": "high",
"remediation": "Use a 32+ character random secret from environment variables",
"path": "config/session.js"
}
]
}
}The middleBrick dashboard provides continuous monitoring of authentication vulnerabilities across your Adonisjs applications. You can track authentication score trends and receive alerts when configurations become insecure.
Adonisjs-Specific Remediation
Remediating broken authentication in Adonisjs requires both configuration changes and code updates. Start with secure session configuration:
// config/session.js - secure configuration
module.exports = {
driver: 'redis', // Use server-side storage instead of cookies
connection: 'redis',
secret: Env.get('SESSION_SECRET'), // Never hardcode secrets
httpOnly: true,
maxAge: 24 * 60 * 60 * 1000,
secure: true, // Only send over HTTPS
sameSite: 'strict',
encryption: true // Encrypt session data
}
// Generate secure secret
// node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"For JWT authentication, use environment-based secrets with proper validation:
// config/auth.js - secure JWT configuration
module.exports = {
authenticator: 'jwt',
secret: Env.get('JWT_SECRET'),
options: {
expiresIn: '1h', // Shorter expiration for better security
algorithm: 'HS256',
issuer: 'your-app-name',
audience: 'your-api-users'
}
}
// Generate secure JWT secret
// node -e "console.log(require('crypto').randomBytes(64).toString('base64'))"Implement proper password hashing with adequate iterations:
// app/Models/User.js
const User = use('Model')
const Hash = use('Hash')
User.addHook('beforeSave', async (userInstance) => {
if (userInstance.dirty.password) {
// Use 12+ rounds for bcrypt - higher is better but slower
userInstance.password = await Hash.make(userInstance.password, 12)
}
})
module.exports = UserProtect routes with proper guard specification:
// routes.js - secure route protection
Route.group(() => {
Route.get('/admin/dashboard', 'AdminController.dashboard')
Route.post('/admin/users', 'AdminController.createUser')
})
.prefix('admin')
.middleware(['auth:admin']) // Specify guard explicitlyFix session fixation by regenerating session IDs after authentication:
// app/Controllers/Http/AuthController.js
const User = use('App/Models/User')
const Antl = use('Antl')
class AuthController {
async login({ auth, request, response, session }) {
const { email, password } = request.all()
// Use auth.attempt() which handles session fixation
try {
await auth.attempt(email, password)
// Regenerate session ID manually as additional protection
const oldSessionId = session.id()
await session.regenerate()
const newSessionId = session.id()
if (oldSessionId === newSessionId) {
console.warn('Session regeneration failed')
}
return response.redirect('/dashboard')
} catch (error) {
return response.badRequest({ error: Antl.forLocale('en').formatMessage('auth.invalid') })
}
}
}
module.exports = AuthControllerImplement rate limiting to prevent credential stuffing attacks:
// start/kernel.js - add rate limiting
const namedMiddleware = {
auth: 'Adonis/Middleware/Auth',
session: 'Adonis/Middleware/Session',
'throttle': 'Adonis/Middleware/Throttle',
'verifyCSRFToken': 'Adonis/Middleware/CsrfToken'
}
// Add to routes.js
Route.post('/login', 'AuthController.login')
.middleware(['throttle:100,5']) // 100 requests per 5 minutesAdd authentication monitoring middleware to detect suspicious patterns:
// middleware/AuthenticationMonitor.js
const moment = require('moment')
class AuthenticationMonitor {
async handle({ auth, request, response }, next) {
const start = Date.now()
await next()
const duration = Date.now() - start
const ip = request.ip()
const url = request.url()
// Log authentication attempts for monitoring
if (url.includes('login') || url.includes('auth')) {
console.log(`Auth attempt: ${ip} - ${url} - ${duration}ms - ${auth.user ? 'SUCCESS' : 'FAILURE'}`)
// Check for unusual patterns
if (duration < 100 && !auth.user) {
console.warn(`Potential brute force from ${ip}: quick failed attempt`)
}
}
}
}
module.exports = AuthenticationMonitorFinally, integrate middleBrick into your development workflow for continuous authentication security assessment:
# package.json scripts
{
"scripts": {
"start": "adonis serve --dev",
"scan:auth": "middlebrick scan http://localhost:3333 --category authentication",
"test": "adonis test && npm run scan:auth"
}
}This ensures authentication security is validated with every test run, catching regressions before they reach production.
Related CWEs: authentication
| CWE ID | Name | Severity |
|---|---|---|
| CWE-287 | Improper Authentication | CRITICAL |
| CWE-306 | Missing Authentication for Critical Function | CRITICAL |
| CWE-307 | Brute Force | HIGH |
| CWE-308 | Single-Factor Authentication | MEDIUM |
| CWE-309 | Use of Password System for Primary Authentication | MEDIUM |
| CWE-347 | Improper Verification of Cryptographic Signature | HIGH |
| CWE-384 | Session Fixation | HIGH |
| CWE-521 | Weak Password Requirements | MEDIUM |
| CWE-613 | Insufficient Session Expiration | MEDIUM |
| CWE-640 | Weak Password Recovery | HIGH |