Mass Assignment in Adonisjs with Basic Auth
Mass Assignment in Adonisjs with Basic Auth — how this specific combination creates or exposes the vulnerability
Mass assignment occurs when an API binds incoming request fields directly to a model or database record without explicit allowlisting. In Adonisjs, this typically happens when using merge in controllers to apply request body to an ORM instance, for example user.merge(request.body). If the request is authenticated only by Basic Auth, the server may trust the authenticated identity but still accept any field name the client sends. An attacker authenticated with a low-privilege Basic Auth credential can therefore add or overwrite sensitive fields such as is_admin, role, or permissions if the controller does not restrict which keys are accepted.
Adonisjs does not perform automatic schema-based filtering; developers must explicitly define which fields are updatable. When Basic Auth is used, authorization checks often focus on the username and password, while the application may inadvertently permit the authenticated user to supply values for sensitive columns. For example, a PATCH /users/:id endpoint that calls user.merge(request.body) without a permitlist enables BOLA/IDOR and mass assignment in one call: the endpoint locates the user by route parameter, confirms the provided HTTP Basic credentials, then merges all body fields into the record. This combination means authentication proves identity, but does not limit what can be changed.
Real-world impact resembles issues tracked under common attack patterns such as CVE-2021-21342 (improper neutralization of special elements), where mass assignment in a REST layer led to privilege escalation. In Adonisjs, if the User model contains fields like isVerified or role, and these are not guarded, an authenticated attacker can modify them simply by including the keys in JSON or form-encoded payloads. The vulnerability is not in Basic Auth itself, but in the unchecked binding of request properties to the model, especially when route-level ownership checks are incomplete.
To detect this during scanning, middleBrick runs checks for Property Authorization and BOLA/IDOR alongside input validation, and maps findings to OWASP API Top 10 and PCI-DSS controls. If your API uses Basic Auth, ensure each update operation specifies a strict permitlist (e.g., only email or profileBio), and validate ownership of the resource before applying changes.
Basic Auth-Specific Remediation in Adonisjs — concrete code fixes
Remediation centers on explicit field allowlisting and strict ownership verification before applying any updates. Avoid generic merges and instead pick only the fields you intend to support. Below are concrete, working Adonisjs examples that combine HTTP Basic Auth with safe mass assignment prevention.
Example 1: Safe update with permitlist
import { schema } from '@ioc:Adonis/Core/Validator'
const userUpdateSchema = schema.create({
email: schema.string.optional(),
profileBio: schema.string.optional(),
})
export default class UsersController {
public async update({ request, params, auth }) {
const payload = await request.validate({ schema: userUpdateSchema })
const user = await auth.getUserOrFail()
// Ensure the authenticated user can only update their own account
if (user.id !== params.id) {
throw new Error('Unauthorized')
}
// Apply only the allowed fields
user.merge(payload)
await user.save()
return user
}
}
Example 2: Using route-level auth with Basic Auth and permitlist
import { schema } from '@ioc:Adonis/Core/Validator'
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
const safeFieldsSchema = schema.create({
email: schema.string.optional(),
})
export default class ProfileController {
public async updateProfile({ request, auth }: HttpContextContract) {
const payload = await request.validate({ schema: safeFieldsSchema })
const user = await auth.getUserOrFail()
// Only permitted fields are merged; no attacker-controlled keys like is_admin can pass
user.merge(payload)
await user.save()
return user
}
}
Example 3: Controller-level guard with ownership check
import { schema } from '@ioc:Adonis/Core/Validator'
const minimalProfileSchema = schema.create({
displayName: schema.string.optional(),
})
export default class AccountController {
public async update({ request, params, auth }) {
const payload = await request.validate({ schema: minimalProfileSchema })
const user = await auth.getUserOrFail()
if (String(user.id) !== String(params.id)) {
throw new Error('Forbidden')
}
// Explicitly set allowed fields instead of merging raw body
user.displayName = payload.displayName
await user.save()
return user
}
}
These patterns ensure that even when credentials are verified via Basic Auth, only explicitly permitted fields can be changed. Combine this with route-level ownership checks and schema validation to close the mass assignment vector. middleBrick can highlight missing permitlists and weak authorization mappings in scans, and the Pro plan supports continuous monitoring to catch regressions when endpoints evolve.
Related CWEs: propertyAuthorization
| CWE ID | Name | Severity |
|---|---|---|
| CWE-915 | Mass Assignment | HIGH |