Crlf Injection in Adonisjs with Mutual Tls
Crlf Injection in Adonisjs with Mutual Tls — how this specific combination creates or exposes the vulnerability
Crlf Injection occurs when an attacker can inject a Carriage Return (CR, \r) and Line Feed (\n) sequence into a header or status-line context, causing the server to prematurely terminate a header block and inject additional headers or split responses. In Adonisjs, this typically maps to the OWASP API Top 10 A03: Injection category and can lead to HTTP response splitting, cache poisoning, or cross-site scripting when injected values are reflected downstream.
Mutual Transport Layer Security (Mutual Tls) requires both the client and server to present valid certificates during the TLS handshake. While Mutual Tls strengthens authentication and ensures the identity of peers, it does not alter how Adonisjs parses and uses incoming request data. A common misconception is that Mutual Tls prevents injection because the connection is authenticated; however, the vulnerability resides in application-level handling of untrusted inputs (e.g., headers, query parameters, or body fields) that are later used to construct status lines or headers. If an API endpoint behind Mutual Tls copies an attacker-controlled certificate field, common name, or a selected header value into a downstream request or response without proper sanitization, Crlf Injection remains possible.
For example, an Adonisjs route that forwards requests to a backend service might take a client-supplied X-Redirect-Host header and use it to build a location value. If Mutual Tls is enforced, the developer might trust the certificate’s subject information and concatenate it into a header. An attacker with a valid certificate could supply example.com%0d%0aSet-Cookie:%20auth=evil in an allowed header, and if Adonisjs does not strip or encode CR/LF, the forwarded request or response can be split. Because Mutual Tls only authenticates endpoints and does not sanitize data, the attack surface persists at the application layer.
Another scenario involves logging or audit trails where certificate details (such as the Common Name) are written to response headers or logs. If those values are reflected without validation and an attacker’s certificate contains injected sequences, the log or response can be manipulated when viewed in certain clients or intermediaries. The key takeaway is that Mutual Tls secures transport and peer identity but does not protect against improper data handling; input validation and output encoding remain essential to mitigate Crlf Injection in Adonisjs.
Mutual Tls-Specific Remediation in Adonisjs — concrete code fixes
Remediation focuses on strict validation, normalization, and avoiding direct concatenation of untrusted values into headers or status lines. Below are concrete patterns for Adonisjs that remain effective under Mutual Tls.
1. Validate and sanitize header inputs
Do not forward or reflect raw header values. Use a denylist/allowlist approach and remove CR/LF characters explicitly.
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
import { string } from '@ioc:Adonis/Core/Helpers')
export default class ApiProxyController {
public async forward(ctx: HttpContextContract) {
const raw = ctx.request.header('x-custom-host')
// Remove CR and LF to prevent header splitting
const sanitized = string.clean(raw, { crlf: true })
if (!sanitized || sanitized !== raw) {
ctx.response.badRequest({ error: 'Invalid header value' })
return
}
// Use sanitized value safely
ctx.response.redirect(`https://${sanitized}/dashboard`)
}
}
The helper string.clean with crlf: true strips CR and LF characters, preventing injected line breaks. Always prefer allowlists (e.g., regex for hostnames) over denylists where possible.
2. Enforce hostname validation for redirects
When building redirect URLs, validate the host against a known set or a strict hostname pattern to avoid injection via certificate-derived fields.
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
import { hostname } from '@ioc:Adonis/Core/Validator')
export default class SecureRedirectController {
public async go(ctx: HttpContextContract) {
const host = ctx.request.hostname()
const target = ctx.request.input('target')
// Validate target hostname; reject if not matching pattern
if (!hostname.validate(target)) {
return ctx.response.badRequest({ error: 'Invalid hostname' })
}
ctx.response.redirect(`https://${target}/dashboard`)
}
}
3. Secure Mutual Tls configuration in Adonisjs
Adonisjs relies on the underlying Node.js TLS options. Provide a strict server configuration that requests client certificates and validates them.
// start/server.ts
import { defineConfig } from '@adonisjs/core/app'
export default defineConfig({
https: {
key: '/path/to/server-key.pem',
cert: '/path/to/server-cert.pem',
ca: '/path/to/ca-bundle.pem',
requestCert: true,
rejectUnauthorized: true,
},
})
requestCert: true and rejectUnauthorized: true enforce Mutual Tls, ensuring only clients with trusted certificates can connect. This setup does not sanitize incoming data; combine it with input validation as shown above.
4. Avoid reflecting certificate fields in headers
If you must log or echo certificate details, ensure they are normalized and do not contain line breaks. For instance, extract the Common Name and validate it before use.
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
export default class CertLoggerController {
public async info(ctx: HttpContextContract) {
const cert = ctx.request.socket.getPeerCertificate()
const cn = cert.subject?.CN
if (cn && /^[a-zA-Z0-9\-._]+$/.test(cn)) {
ctx.response.json({ commonName: cn })
} else {
ctx.response.forbidden({ error: 'Invalid certificate subject' })
}
}
}
This pattern ensures only safe characters are reflected, eliminating CR/LF injection risks even when Mutual Tls is active.