Double Free in Adonisjs with Basic Auth
Double Free in Adonisjs with Basic Auth — how this specific combination creates or exposes the vulnerability
A Double Free in Adonisjs when combined with Basic Auth typically arises from how the framework processes authentication state and request resources across multiple middleware or route handlers. When Basic Auth credentials are parsed on each request, Adonisjs may allocate internal structures (such as user identity objects or session-like request context) to represent the authenticated principal. If the application logic or a custom hook erroneously triggers a second cleanup or deallocation of these structures—such as calling a user-loading method more than once per request, or freeing resources in an after hook after the response has already been finalized—the runtime can encounter a double free condition. This often maps to C++ level memory safety issues when Adonisjs (via Node.js native addons or underlying frameworks) handles the authenticated request object in an unsafe manner, or in edge cases where the runtime reuses or releases references incorrectly.
In practical terms, an attacker can weaponize this by sending repeated or malformed Authorization headers (e.g., Authorization: Basic dXNlcjpwYXNz) while the application performs redundant user resolution or token binding. Because the scan is unauthenticated, middleBrick tests the endpoint’s public surface, including how it handles malformed or repeated auth headers. If the server’s route or authentication layer re-initializes or disposes of the same user object across middleware layers, the runtime may exhibit undefined behavior. This can lead to crashes, information leaks, or inconsistent state that exposes internal objects. The 12 security checks run by middleBrick will surface related instability findings under Authentication and BOLA/IDOR, noting whether repeated auth inputs trigger anomalous behavior or resource handling anomalies.
For example, consider a custom auth provider that loads a user, attaches it to the context, and then later reloads or clears it within the same lifecycle due to misconfigured guards or hooks:
// Adonisjs pseudo-code illustrating a risky pattern
async handle({ request, auth }, next) {
// First load
const user = await auth.getUser()
request.ctx.user = user
// Later, another call that may re-initialize or double-free internal refs
await auth.verify()
await next()
}
Such patterns can interact poorly with how Adonisjs manages request-scoped resources, especially under concurrent or malformed Basic Auth inputs. middleBrick’s parallel checks help surface these by correlating Authentication and BOLA/IDOR findings with runtime behavior when probing endpoints that accept Basic credentials.
Basic Auth-Specific Remediation in Adonisjs — concrete code fixes
To mitigate Double Free and related instability issues when using Basic Auth in Adonisjs, ensure that authentication state is loaded exactly once per request and that no subsequent operation attempts to re-free or re-initialize the same user context. Centralize user resolution in a single middleware or hook, avoid redundant auth calls within the same request lifecycle, and guard against repeated or malformed headers that can trigger unsafe code paths.
Use Adonisjs built-in auth utilities consistently and avoid manual re-verification within the same request. Below is a safe pattern using HTTP Basic Auth via the auth manager, where user resolution happens once and the context is reused safely:
// routes.ts
import Route from '@ioc:Adonis/Core/Route'
Route.get('/secure', async ({ request, auth }) => {
// Single, authoritative load
const user = await auth.getUserOrFail()
return { user: user.serialize() }
}).middleware(['auth:basic'])
Configure the Basic Auth provider in config/auth.ts to use a custom Lucid model resolver without redundant verification steps:
// config/auth.ts
import { AuthConfig } from '@ioc:Adonis/Addons/Auth'
import User from 'App/Models/User'
const authConfig: AuthConfig = {
default: 'basic',
guards: {
basic: {
driver: 'basic',
userModel: User,
passwordEnvField: 'USER_PASSWORD',
passwordHashFn: 'bcrypt.hash'
}
}
}
export default authConfig
If you use custom hooks, ensure they do not call auth.getUser multiple times or attempt to re-verify credentials within the same request. Instead, resolve once and propagate via request context:
// start/hooks.ts
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
export const authHook = async (ctx: HttpContextContract, next: () => Promise<void>) => {
const user = await ctx.auth.getUser() // resolved once
ctx.request.ctx.user = user
await next()
}
Validate input early and reject malformed Authorization headers before they propagate into business logic. This reduces the chance of unsafe resource handling:
// middleware/validate-basic-header.ts
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
export default async function validateBasicHeader(ctx: HttpContextContract, next: () => Promise<void>) {
const authHeader = ctx.request.header('authorization')
if (authHeader && !authHeader.startsWith('Basic ')) {
ctx.response.unauthorized({ message: 'Invalid authorization header format' })
return
}
await next()
}