HIGH email injectionadonisjsmutual tls

Email Injection in Adonisjs with Mutual Tls

Email Injection in Adonisjs with Mutual Tls

Email Injection in AdonisJS with Mutual TLS arises when user-controlled input is used to construct email commands or headers without validation, and the application is configured to present a client certificate during Mutual TLS handshakes. Mutual TLS ensures the client presents a valid certificate, but it does not sanitize data that eventually reaches email-related endpoints. An attacker can supply newline characters (e.g., %0a or \r\n) in fields like to, cc, or custom headers to inject additional recipients or smtp commands. The presence of Mutual TLS may give a false sense of strong authentication, leading developers to overlook input validation for email fields.

Consider an AdonisJS mail service that builds messages using user input:

const mail = use('Mail')
mail.send((message) => {
  message
    .to(userInputEmail)
    .subject(userInputSubject)
    .html(userInputBody)
})

If userInputEmail contains [email protected]%0aCc:%[email protected], some SMTP servers or mail libraries may interpret the newline and inject an additional recipient. Mutual TLS (mTLS) ensures the client certificate is verified before the request proceeds, but it does not inspect the email content. Therefore, the combination exposes an injection vector: strong transport-layer identity paired with weak data validation at the application layer.

In a scan, this may appear as an unauthenticated attack surface where email headers can be manipulated. The scanner tests inputs such as to:%20%0aBcc:%[email protected] and checks whether the resulting email behavior deviates from the intended recipient list. Because mTLS is enforced at the transport layer, developers might assume all requests are trustworthy, inadvertently relaxing validation rigor for email fields.

Remediation requires strict validation of email addresses and header components, independent of transport security. Always treat input as untrusted, even when Mutual TLS is in place. Normalize and parse email inputs, reject unexpected newline characters, and avoid building email commands via string concatenation.

Mutual Tls-Specific Remediation in Adonisjs — concrete code fixes

To secure AdonisJS applications with Mutual TLS while preventing Email Injection, apply strict input validation and explicitly configure TLS options. Below are concrete code examples that combine mTLS setup with safe email handling.

1. Configure Mutual TLS in AdonisJS (server-side)

Ensure the server requests and validates client certificates. Use AdonisJS SSL options to enforce mTLS without relying on external assumptions.

// start/server.ts
import { HttpServer } from '@adonisjs/core/build/standalone'

export const http = new HttpServer({
  ssl: {
    key: '/path/to/server-key.pem',
    cert: '/path/to/server-cert.pem',
    ca: '/path/to/ca-cert.pem',
    requestCert: true,
    rejectUnauthorized: true,
  },
})

requestCert: true and rejectUnauthorized: true enforce that clients must present a certificate signed by the trusted CA. This is the Mutual TLS setup.

2. Validate and sanitize email inputs

Never trust user input for email fields. Use a validation schema that rejects newline characters and ensures proper email formatting.

// validate emails strictly
import { schema } from '@ioc:Adonis/Core/Validator'

const emailSchema = schema.create({
  email: schema.string({}, [rules.format({ pattern: /^[^\r\n]+@[^\r\n]+$/ }) ]),
  subject: schema.string({}, [rules.escape] ),
})

export default class MailController {
  public async send({ request, response }) {
    const payload = await request.validate({ schema: emailSchema })

    const mail = use('Mail')
    mail.send((message) => {
      message
        .to(payload.email)
        .subject(payload.subject)
        .html(userInputBody) // ensure body is also sanitized
    })

    return response.ok({ message: 'Queued' })
  }
}

The regex ^[^\r\n]+ explicitly blocks carriage return and newline characters, preventing header injection. Rules.format ensures the email contains an @ symbol and a plausible structure.

3. Use a trusted mail library and avoid header concatenation

Build emails using library-provided methods rather than raw strings. For example, with Nodemailer-like transports, set recipients via structured arrays:

import Mail from '@ioc:AdonisJS/Addons/Mail'

export default class MailController {
  public async send({ request }) {
    const { email, subject, html } = await request.validate({
      schema: schema.create({
        email: schema.string({}, [rules.format({ pattern: /^[^\r\n]+$/ })]),
        subject: schema.string(),
        html: schema.string(),
      })
    })

    await Mail.sendLater((message) => {
      message
        .to([email]) // array ensures single recipient
        .from('[email protected]')
        .subject(subject)
        .html(html)
    })
  }
}

This approach avoids concatenation risks and works safely with mTLS, as transport security is separate from data validation.

Finally, combine mTLS with runtime scanning. Use the CLI to verify that no newline-based injection is possible even when client certificates are presented: middlebrick scan <url>. The dashboard can track email-related findings over time, and the GitHub Action can enforce a minimum score before merging changes that modify mail routes.

Frequently Asked Questions

Does Mutual TLS alone prevent Email Injection in AdonisJS?
No. Mutual TLS authenticates the client at the transport layer but does not validate or sanitize email header content. Input validation must be applied explicitly to prevent injection.
How can I test for Email Injection in an mTLS-protected AdonisJS API?
Submit inputs containing newline sequences (e.g., %0aBcc:%[email protected]) in email fields and verify that the recipient list does not change. middleBrick scans can test this behavior in unauthenticated mode and highlight header injection risks.