Broken Authentication in Adonisjs with Basic Auth
Broken Authentication in Adonisjs with Basic Auth — how this specific combination creates or exposes the vulnerability
AdonisJS is a Node.js web framework that does not provide built-in protection against common authentication weaknesses when you implement HTTP Basic Authentication yourself. Using Basic Auth without additional controls can create or expose broken authentication because the mechanism relies on sending credentials in an easily decoded format on every request. If TLS is not enforced consistently, credentials are exposed in transit. If the implementation does not enforce strong credential storage, attackers can obtain or brute-force weak hashes. If rate limiting or account lockout is missing, online password guessing becomes feasible.
Basic Auth sends an RFC 7617–formatted header (Authorization: Basic base64(username:password)). Base64 is not encryption; it is trivial to decode. Therefore, any interceptor who sees the header can recover the credentials unless TLS protects the entire session. In AdonisJS, a route that reads the header without enforcing HTTPS and without additional checks exposes an unauthenticated attack surface that middleBrick’s unauthenticated scan can detect as a potential authentication bypass or credential exposure finding under Authentication and Data Exposure checks.
Within the context of the OWASP API Top 10, broken authentication often maps to broken access control and insufficient encryption. AdonisJS applications that use Basic Auth for API endpoints must ensure that credentials are verified securely on each request, that sessions or tokens are not reused insecurely, and that rate limiting is applied to mitigate brute-force attacks. Without these controls, an attacker who intercepts or guesses a valid username and password can impersonate a user indefinitely, which an API security scan can surface as a high-severity finding.
Additionally, if the application reuses the same credentials across environments or fails to rotate secrets, the risk increases. MiddleBrick’s 12 security checks, including Authentication and Data Exposure, are designed to surface these issues in an unauthenticated scan by analyzing the OpenAPI specification and runtime behavior. The scanner does not fix the problem but provides prioritized findings with remediation guidance to help developers strengthen authentication in AdonisJS when Basic Auth is required.
Basic Auth-Specific Remediation in Adonisjs — concrete code fixes
To reduce the risk of broken authentication when using Basic Auth in AdonisJS, enforce HTTPS, store credentials safely, and add rate limiting. Below are concrete, working examples you can apply directly in your routes and providers.
1. Enforce HTTPS in production
Ensure all requests use TLS. In AdonisJS, you can enforce HTTPS at the route level or via middleware. The following route condition checks for secure connections before validating credentials:
import Route from '@ioc:Adonis/Core/Route'
Route.get('/secure-endpoint', async ({ request, response }) => {
if (!request.secure()) {
return response.status(403).send({ error: 'HTTPS required' })
}
// Proceed with Basic Auth validation
})
2. Secure credential verification with Base64 decoding
Parse and verify credentials on each request without storing passwords in plaintext. Use a constant-time comparison where possible and avoid leaking timing information. This example decodes the header and checks credentials against environment variables:
import Route from '@ioc:Adonis/Core/Route'
import { base64 } from '@ioc:Adonis/Core/Helpers'
Route.get('/api/data', async ({ request, response }) => {
const authHeader = request.headers().authorization
if (!authHeader || !authHeader.startsWith('Basic ')) {
return response.status(401).header('WWW-Authenticate', 'Basic').send({ error: 'Unauthorized' })
}
const base64Token = authHeader.split(' ')[1]
const token = Buffer.from(base64Token, 'base64').toString('utf-8')
const [username, password] = token.split(':')
const validUser = process.env.BASIC_AUTH_USER
const validPass = process.env.BASIC_AUTH_PASS
if (username !== validUser || password !== validPass) {
return response.status(401).header('WWW-Authenticate', 'Basic').send({ error: 'Invalid credentials' })
}
return response.send({ data: 'Protected resource' })
})
3. Add rate limiting to mitigate brute-force attacks
Basic Auth is vulnerable to online guessing if attackers can attempt many passwords. Implement rate limiting per IP or per credential to increase attacker cost. AdonisJS allows easy integration with third-party rate limiters; conceptually, limit login attempts per source:
import Route from '@ioc:Adonis/Core/Route'
import RateLimiter from 'some-rate-limiter'
const limiter = new RateLimiter({ windowMs: 60 * 1000, max: 10 }) // 10 requests per minute
Route.post('/login', async ({ request, response }) => {
const ip = request.ip()
if (!await limiter.check(ip)) {
return response.status(429).send({ error: 'Too many attempts' })
}
const authHeader = request.headers().authorization
if (!authHeader || !authHeader.startsWith('Basic ')) {
return response.status(401).header('WWW-Authenticate', 'Basic').send({ error: 'Unauthorized' })
}
const base64Token = Buffer.from(authHeader.split(' ')[1], 'base64').toString('utf-8')
const [username, password] = base64Token.split(':')
if (username !== process.env.BASIC_AUTH_USER || password !== process.env.BASIC_AUTH_PASS) {
limiter.fail(ip)
return response.status(401).header('WWW-Authenticate', 'Basic').send({ error: 'Invalid credentials' })
}
limiter.succeed(ip)
return response.send({ data: 'Authenticated' })
})
4. Use middleware for reusable Basic Auth checks
To avoid repeating logic, create an auth middleware that validates credentials and enforces HTTPS. This keeps route handlers clean and ensures consistent protection across endpoints that require Basic Auth.
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
import { middleware } from '@ioc:Adonis/Core/Middleware'
const basicAuth = middleware(async (ctx, next) => {
if (!ctx.request.secure()) {
return ctx.response.status(403).send({ error: 'HTTPS required' })
}
const authHeader = ctx.request.headers().authorization
if (!authHeader || !authHeader.startsWith('Basic ')) {
ctx.response.header('WWW-Authenticate', 'Basic')
return ctx.response.status(401).send({ error: 'Unauthorized' })
}
const token = Buffer.from(authHeader.split(' ')[1], 'base64').toString('utf-8')
const [username, password] = token.split(':')
if (username !== process.env.BASIC_AUTH_USER || password !== process.env.BASIC_AUTH_PASS) {
return ctx.response.status(401).header('WWW-Authenticate', 'Basic').send({ error: 'Invalid credentials' })
}
await next()
})
export default basicAuth
Apply the middleware to routes that require protection. This pattern centralizes security checks and makes it easier to audit and update authentication behavior.
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 |