Insecure Design in Adonisjs with Mutual Tls
Insecure Design in Adonisjs with Mutual Tls — how this specific combination creates or exposes the vulnerability
Insecure design in an AdonisJS application using mutual TLS (mTLS) often arises from treating mTLS as the sole authentication mechanism while neglecting authorization, input validation, and secure defaults. mTLS provides transport-layer identity by requiring both client and server to present valid certificates. However, if the application does not enforce additional checks—such as verifying certificate attributes, mapping certificates to users or roles, and applying strict authorization—mTLS alone cannot prevent vertical or horizontal privilege escalation (BOLA/IDOR) and can give a false sense of security.
For example, an AdonisJS route that relies only on mTLS may trust the certificate’s Common Name (CN) or subject without validating it against an authorization model. If the certificate maps to a user ID used in database queries without further checks, an attacker who compromises a low-privilege certificate can iterate over identifiers to access other users’ resources (BOLA/IDOR). This is an insecure design because mTLS handles authentication but not authorization, and the application must implement both.
Another insecure pattern is failing to validate and sanitize inputs even when mTLS is in place. An attacker might embed malicious payloads in request bodies or query parameters, and if the application does not apply input validation, property-based authorization, or safe data handling, injection or business logic flaws can occur. For instance, an endpoint that accepts an id parameter and directly uses it in an AdonisJS Lucid query without validation could enable IDOR if the identifier is not scoped to the requesting certificate’s mapped entity.
Insecure design also appears in certificate lifecycle management. Hardcoding certificate paths, weak file permissions on certificate stores, or failing to rotate certificates can expose private keys. If the AdonisJS app loads certificates via environment variables without runtime protection, misconfigured environments might leak sensitive material. Additionally, missing revocation checks (e.g., CRL or OCSP) can allow compromised certificates to remain trusted, which is an insecure operational design even when mTLS is technically implemented.
These issues map to broader categories such as OWASP API Top 10 API1:2023 Broken Object Level Authorization and API2:2023 Broken User Authentication. PCI-DSS and SOC2 controls also emphasize the need for strong access control and key management. middleBrick scans detect such insecure design patterns by correlating OpenAPI/Swagger specs (with full $ref resolution) against runtime behavior, flagging missing authorization on authenticated endpoints, insufficient input validation, and unsafe certificate usage.
Mutual Tls-Specific Remediation in Adonisjs — concrete code fixes
To remediate insecure design when using mutual TLS in AdonisJS, combine mTLS enforcement with explicit authorization, input validation, and secure certificate handling. Below are concrete code examples that demonstrate a secure approach.
1. Enforce mTLS in AdonisJS HTTP server
Configure the AdonisJS server to require client certificates. In start/server.ts, set up the HTTPS server with request certificate verification:
import { HttpServer } from '@adonisjs/core/build/standalone';
import { readFileSync } from 'fs';
const server = new HttpServer();
server.app.use(async (ctx, next) => {
const req = ctx.request;
// Ensure a client certificate is present and validated by TLS layer
if (!req.socket.getPeerCertificate()) {
ctx.response.status = 401).send({ error: 'Client certificate required' });
return;
}
await next();
});
server.start({
https: {
key: readFileSync('path/to/server-key.pem'),
cert: readFileSync('path/to/server-cert.pem'),
ca: readFileSync('path/to/ca-chain.pem'),
requestCert: true,
rejectUnauthorized: true,
},
});
export default server;
This ensures the TLS layer rejects connections without a valid client certificate signed by the trusted CA, reducing the risk of unauthenticated access.
2. Map certificate identity to application authorization
Do not rely solely on the certificate CN; extract a stable identifier (e.g., a serial number or subjectAltName) and map it to an application user or role. Use an authorization check before accessing resources:
import { HttpContextContract } from '@adonisjs/core/http';
import User from 'App/Models/User';
export default class ProfilesController {
public async show({ request, params, auth }: HttpContextContract) {
const clientCert = request.socket.getPeerCertificate();
// Map certificate serial to user
const user = await User.query()
.where('cert_serial', clientCert.serialNumber)
.preload('roles')
.firstOrFail();
// Enforce ownership or role-based checks
if (user.id !== auth.user?.id && !user.roles.some(r => r.name === 'admin')) {
throw new Error('Unauthorized');
}
return user;
}
}
This prevents BOLA/IDOR by ensuring that certificate identity is mapped to an authorization model and that users can only access their own data unless explicitly permitted.
3. Validate and sanitize all inputs
Even with mTLS, validate and sanitize inputs to prevent injection and logic flaws. Use AdonisJS schema validation:
import { schema } from '@ioc:Adonis/Core/Validator';
const profileSchema = schema.create({
id: schema.number([
() => rules.unsigned(),
() => rules.exists({ table: 'profiles', column: 'id' }),
]),
});
export default class ProfilesController {
public async show({ request, params }: HttpContextContract) {
const payload = await request.validate({ schema: profileSchema });
const profile = await Profile.findOrFail(payload.id);
return profile;
}
}
This ensures that resource identifiers are valid and scoped, mitigating injection and authorization bypass risks.
4. Secure certificate storage and rotation
Avoid hardcoding certificate paths. Use runtime secrets and restrict file permissions. For example, load certificates from a secure volume with limited access and rotate them periodically. In containerized deployments, use secrets management to inject paths and keys at runtime without exposing them in configuration files.
5. Implement revocation checks
Where feasible, perform revocation checks during the TLS handshake or as an additional application-layer verification to handle compromised certificates promptly.