Auth Bypass in Adonisjs with Mutual Tls
Auth Bypass in Adonisjs with Mutual Tls — how this specific combination creates or exposes the vulnerability
Mutual Transport Layer Security (mTLS) requires both the client and the server to present valid certificates during the TLS handshake. In AdonisJS, mTLS is typically enforced at the reverse proxy or load balancer (e.g., NGINX, Traefik) or via Node.js HTTPS server options. If the application layer in AdonisJS does not independently verify the client certificate after the TLS handshake, an auth bypass can occur: an attacker who can terminate or spoof TLS (e.g., using a valid certificate issued by a trusted CA, or by connecting directly to the application port without mTLS enforcement) can send requests that appear authenticated to AdonisJS middleware.
Specifically, this happens when AdonisJS relies solely on the presence of req.client.authorized or a proxy-supplied header (like x-forwarded-cert) without validating the certificate chain, common name (CN), or extended key usage. AdonisJS route guards or middleware may check for the existence of a user derived from a session or token but skip verifying that the mTLS credential maps to an authorized identity. This mismatch between transport-layer authentication and application-level authorization enables horizontal or vertical privilege escalation (BOLA/IDOR) when an attacker uses a valid but low-privilege certificate to access another user’s resources.
Additionally, if the AdonisJS app accepts HTTP (not HTTPS) for development or legacy reasons while mTLS is expected only on HTTPS endpoints, an attacker can bypass mTLS entirely by sending requests over plain HTTP. This often occurs when route-level HTTPS enforcement is missing or when load balancers strip or mishandle TLS metadata. The OpenAPI/Swagger spec analysis integrated in middleBrick highlights such inconsistencies by cross-referencing security schemes with runtime behavior, helping to detect missing securitySchemes for mTLS and unauthenticated paths that should require client certificates.
Real-world attack patterns tied to this issue include:
- Using a stolen or improperly scoped certificate to call administrative endpoints (BFLA/Privilege Escalation).
- Exploiting missing certificate validation in custom AdonisJS policies to perform IDOR by iterating over user identifiers.
- SSRF-induced mTLS bypass when internal services accept unverified client connections, leading to unauthorized access to downstream APIs.
These findings are surfaced by middleBrick’s 12 security checks, which run in parallel and map to compliance frameworks such as OWASP API Top 10 and PCI-DSS. The scanner tests unauthenticated attack surfaces and, when an OpenAPI spec is provided, resolves $ref definitions to ensure mTLS security schemes are consistently applied across operations.
Mutual Tls-Specific Remediation in Adonisjs — concrete code fixes
To securely implement mTLS in AdonisJS and prevent auth bypass, enforce client certificate validation at the application layer and align route security with transport-layer guarantees. Below are concrete steps and code examples.
1. Enforce mTLS at the HTTPS server level
When creating an HTTPS server in AdonisJS (e.g., in start.ts), require and verify client certificates using the requestCert and rejectUnauthorized options. This ensures only clients with valid, trusted certificates can complete the handshake.
import { HttpServer } from '@adonisjs/core/types'
import { createServer } from 'node:https'
import { readFileSync } from 'node:fs'
export default class HttpsServerProvider {
public async register() {
// Not used directly; server creation handled via provider or custom command
}
}
// In a custom server bootstrap or provider
const httpsOptions = {
key: readFileSync('path/to/server.key'),
cert: readFileSync('path/to/server.crt'),
ca: readFileSync('path/to/ca-bundle.crt'),
requestCert: true,
rejectUnauthorized: true, // Reject clients without valid cert
};
const server = createServer(httpsOptions, (req, res) => {
// At this point, req.client contains the verified certificate
const cert = req.client?.getRawCert?.()
if (!cert) {
res.statusCode = 403
res.end('Client certificate required')
return
}
// Optionally map certificate fields (e.g., CN) to user identity
const username = extractUsernameFromCert(cert)
req.user = { username, roles: mapRoles(username) }
// Continue routing via AdonisJS adapter
})
server.listen(443, () => console.log('mTLS server listening on port 443'))
function extractUsernameFromCert(cert: Buffer): string {
// Parse certificate subject CN (simplified example)
const subject = cert.toString('utf8')
const match = subject.match(/CN=([^,\/]+)/)
return match ? match[1] : 'unknown'
}
2. Validate certificate details in middleware
Create an AdonisJS middleware that verifies certificate extensions (e.g., Extended Key Usage) and binds the certificate to a user in your database. This prevents unauthorized certificates even if TLS handshake succeeds.
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
import { string } from '@ioc:Adonis/Core/Helpers'
export default class MtlsAuthMiddleware {
public async handle({ request, response, auth }: HttpContextContract, next: () => Promise) {
const cert = request.request.socket.getPeerCertificate?.()
if (!cert || !cert.subject) {
return response.forbidden({ error: 'Client certificate missing' })
}
// Example: ensure certificate has specific EKU or SAN
const hasRequiredEku = checkEKU(cert.infoAccess)
if (!hasRequiredEku) {
return response.forbidden({ error: 'Insufficient certificate permissions' })
}
// Map CN or serial to a user and attach to auth
const username = extractUsernameFromCert(cert.subject)
const user = await User.findBy('username', username)
if (!user) {
return response.forbidden({ error: 'Certificate not authorized' })
}
await auth.use('api').login(user, { persist: false })
await next()
}
}
function checkEKU(infoAccess: any): boolean {
// Implement actual EKU/SAN validation logic here
return true
}
3. Align route-level security with mTLS
Ensure routes requiring mTLS are grouped and that insecure HTTP routes are restricted. Use AdonisJS route middleware to enforce that authenticated requests also present a verified certificate.
import Route from '@ioc:Adonis/Core/Route'
Route.group(() => {
Route.get('/account', 'AccountController.show').middleware('mtls')
Route.put('/account', 'AccountController.update').middleware('mtls')
}).prefix('api/v1').middleware({ mtls: true })
4. Validate and sanitize certificate-derived inputs
Treat certificate fields (e.g., CN, email) as untrusted input. Validate and sanitize before using them in queries to avoid injection and ensure proper mapping to your user model.
import { schema, rules } from '@ioc:Adonis/Core/Validator'
const certUserSchema = schema.create({
username: schema.string({ trim: true }, [rules.alpha(), rules.minLength(3)]),
})
// After extracting and mapping certificate data
const validated = await validator.validate({ schema: certUserSchema, data: { username } })
5. Continuous monitoring and spec-driven checks
Use middleBrick’s CLI to scan your AdonisJS endpoints and verify that mTLS security schemes are correctly defined in OpenAPI 3.x and that all relevant paths require client certificates. The CLI can be run in scripts or CI/CD to fail builds if insecure routes are exposed.
# Example: scan an AdonisJS API endpoint from terminal
middlebrick scan https://api.example.com/openapi.json
For teams using GitHub Actions, add the security check as a PR gate; for advanced setups, use the MCP Server to scan APIs directly from IDEs like Cursor or Claude.
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 |