Credential Stuffing in Adonisjs with Mutual Tls
Credential Stuffing in Adonisjs with Mutual Tls — how this specific combination creates or exposes the vulnerability
Credential stuffing is an automated attack where attackers use lists of breached username and password pairs to gain unauthorized access. In AdonisJS applications that also use Mutual TLS (mTLS), relying solely on client certificates for authentication can create a false sense of security. When mTLS is implemented but the application still accepts password-based authentication (or weakly guards the login endpoint), attackers can target the password path while the mTLS channel remains unused or misconfigured.
AdonisJS does not inherently protect against credential stuffing; it is a framework that processes authentication logic written by developers. If session-based or token-based authentication is combined with mTLS without rate limiting, IP throttling, or strong password policies, attackers can abuse the login route even when mTLS is available. For example, an endpoint that validates a client certificate but then falls back to form-based credentials without additional friction becomes a two-path attack surface.
Attackers may also probe unauthenticated routes to enumerate users or trigger account lockouts. Without request throttling per user or certificate, automated scripts can iterate through credentials rapidly. mTLS can help identify the client, but if the application does not tie certificate identity to rate limiting or suspicious behavior detection, credential stuffing can still succeed against password-based flows.
Furthermore, logging and monitoring gaps can obscure ongoing attacks. If failed login events are not correlated with client certificate metadata, anomalous patterns—such as many passwords tried against one certificate or one user behind many certificates—may go unnoticed. Proper instrumentation and strict authentication policies are required to detect and mitigate credential stuffing in AdonisJS environments using mTLS.
Mutual Tls-Specific Remediation in Adonisjs — concrete code fixes
To secure AdonisJS applications with mTLS and reduce credential stuffing risk, enforce strict client certificate validation and avoid fallback to weak authentication paths. Below are concrete code examples for configuring HTTPS with mTLS in AdonisJS using the built-in HTTP server hook.
Enabling mTLS in AdonisJS
Configure the HTTPS server to request and verify client certificates. The example assumes you have a CA certificate that signed client certificates, and each client presents a valid certificate during the TLS handshake.
// start/hooks.ts or server.ts
import { defineConfig } from '@adonisjs/core/app'
export default defineConfig({
https: {
enabled: true,
cert: '/path/to/server-cert.pem',
key: '/path/to/server-key.pem',
ca: '/path/to/ca-cert.pem',
requestCert: true,
rejectUnauthorized: true,
},
})
The requestCert: true setting tells Node.js to request a client certificate. With rejectUnauthorized: true, the server rejects connections if the client certificate is invalid or not signed by the trusted CA. This ensures only clients with valid certificates can proceed.
Validating Certificate Properties in AdonisJS
After the TLS handshake, you can inspect the client certificate in a route or middleware to enforce additional authorization. The certificate fields are available on the request object under request.socket.getPeerCertificate().
// middleware/verify-client-cert.ts
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
export default async function verifyClientCert({ request, response, next }: HttpContextContract) {
const cert = request.socket.getPeerCertificate()
if (!cert || Object.keys(cert).length === 0) {
return response.unauthorized('Client certificate required')
}
// Example: enforce specific common name or SAN
const commonName = cert.subject?.CN
if (!commonName || !commonName.startsWith('client-')) {
return response.unauthorized('Invalid client certificate subject')
}
// Optionally map certificate identity to user roles
request.ctx.clientCert = cert
await next()
}
Register this middleware on protected routes to ensure requests include valid client certificates and to enforce identity-based checks.
Combining mTLS with Rate Limiting and Strong Authentication
Even with mTLS, apply rate limiting on authentication endpoints to deter credential stuffing. Use AdonisJS route middleware to track attempts per certificate or IP.
// middleware/rate-limit-auth.ts
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
const attempts = new Map()
export default function rateLimitAuth({ request, response, next }: HttpContextContract) {
const key = request.clientIp || request.url()
const now = Date.now()
const windowMs = 5 * 60 * 1000 // 5 minutes
const maxAttempts = 10
const record = attempts.get(key) || { count: 0, last: now }
if (now - record.last > windowMs) {
record.count = 0
}
record.count += 1
record.last = now
attempts.set(key, record)
if (record.count > maxAttempts) {
return response.status(429).send('Too many requests')
}
return next()
}
Apply this middleware to login routes and combine it with mTLS identity checks to create layered defenses. Additionally, enforce strong password policies and consider multi-factor authentication to further reduce credential stuffing success.
Finally, map findings to compliance frameworks such as OWASP API Top 10 and PCI-DSS. The Pro plan can help track these metrics over time via the Dashboard and provide prioritized remediation guidance without replacing required manual review.