HIGH server side template injectionhapimutual tls

Server Side Template Injection in Hapi with Mutual Tls

Server Side Template Injection in Hapi with Mutual Tls

Server Side Template Injection (SSTI) in Hapi becomes more complex to assess when Mutual Transport Layer Security (Mutual TLS) is enforced. Mutual TLS authenticates both the client and the server, which can create a false sense of security and alter how the attack surface is evaluated during a scan. middleBrick scans the unauthenticated attack surface; with Mutual TLS, endpoints that incorrectly accept or relay unverified input may still be reachable if client certificates are not strictly validated at the edge or if a reverse proxy terminates TLS and forwards requests without preserving certificate-derived metadata.

Hapi routes often render dynamic content using view engines like Handlebars or embedded template logic. If user-controlled data (e.g., query parameters, headers, or JSON payload fields) is concatenated into template strings without proper escaping, an attacker can inject template code. With Mutual TLS, developers might assume that only trusted clients can reach the route, but if the server uses the client certificate only for authentication and not for authorization checks, an authenticated but malicious client can exploit SSTI to achieve remote code execution in the rendering context.

Consider a Hapi route that uses request.auth.credentials from a Mutual TLS client certificate to personalize a report but injects unchecked fields into a Handlebars template:

const Hapi = require('@hapi/hapi');
const Path = require('path');

const init = async () => {
  const server = Hapi.server({
    port: 443,
    tls: {
      cert: './server-cert.pem',
      key: './server-key.pem',
      requestCert: true,
      rejectUnauthorized: false // misconfigured for this example
    }
  });

  server.views({
    engines: { hbs: require('handlebars') },
    relativeTo: Path.join(__dirname, 'templates'),
    path: './views',
    compileOptions: {
      noEscape: true // dangerous when user data is used
    }
  });

  server.route({
    method: 'GET',
    path: '/report/{id}',
    options: {
      auth: false,
      handler: (request, h) => {
        const user = request.auth.credentials; // { cn: '{{7*7}}' }
        return h.view('report', { title: user.cn, data: request.query.value });
      }
    }
  });

  await server.start();
};
init();

If request.auth.credentials.cn or request.query.value is sourced from an attacker-influenced field and not escaped, SSTI can occur. In a Mutual TLS setup, the server might trust the certificate but still pass raw certificate fields into templates. middleBrick detects this by correlating OpenAPI/Swagger definitions (where security schemes reference mutual TLS requirements) with runtime behavior, highlighting routes where authenticated inputs reach template rendering without escaping.

An attacker with a valid client certificate could supply {{7*7}} or a more dangerous payload such as {{this.constructor.constructor('return process')()}} in a certificate Common Name field, and if the template engine evaluates it, arbitrary code execution may follow. Because Mutual TLS is often perceived as strong boundary defense, developers may skip input validation, increasing the impact of SSTI. middleBrick’s LLM/AI Security checks are not applicable here; this is a classic injection flaw mapped to OWASP API Top 10 #1 (Broken Object Level Authorization) when authorization logic is conflated with authentication signals.

Mutual Tls-Specific Remediation in Hapi

Remediation focuses on strict certificate validation, context-aware authorization, and safe template rendering. Do not rely on Mutual TLS alone to prevent injection; treat certificate-derived data as untrusted unless explicitly verified.

  • Set rejectUnauthorized: true and validate client certificates against a trusted CA. Use certificate fields for authorization decisions only after additional checks.
  • Escape all user-influenced data before passing it to templates. Use Hapi’s built-in escaping or configure your view engine to auto-escape by default.
  • Apply strict schema validation (e.g., using Joi) on query, headers, and payload fields, even when Mutual TLS is in use.

Here is a hardened example:

const Hapi = require('@hapi/hapi');
const Path = require('path');

const init = async () => {
  const server = Hapi.server({
    port: 443,
    tls: {
      cert: './server-cert.pem',
      key: './server-key.pem',
      requestCert: true,
      rejectUnauthorized: true, // enforce client certificate validation
      ca: ['./ca-cert.pem']    // explicit trusted CA
    }
  });

  server.views({
    engines: { hbs: require('handlebars') },
    relativeTo: Path.join(__dirname, 'templates'),
    path: './views',
    compileOptions: {
      noEscape: false, // ensure escaping is enabled
      strict: {
        root: 'allow',
        unknown: 'ignore'
      }
    }
  });

  server.route({
    method: 'GET',
    path: '/report/{id}',
    options: {
      auth: {
        mode: 'required',
        strategy: 'tls',
        scope: ['report-viewer']
      },
      handler: (request, h) => {
        // certificate fields are available but treated as opaque identifiers
        const subject = request.auth.credentials.subject; // { cn: 'alice' }
        const safeTitle = subject.cn.replace(/[^a-zA-Z0-9_]/g, '');
        const validatedValue = request.query.value;
        // Validate and sanitize validatedValue with Joi or similar before passing to template
        return h.view('report', { title: safeTitle, data: validatedValue });
      }
    }
  });

  await server.start();
};
init();

In this example, Mutual TLS is used strictly for authentication. Authorization scope is enforced via the scope parameter, and certificate-derived fields are sanitized before reaching the template. No assumptions are made about trust based solely on TLS client authentication, which aligns with how middleBrick correlates security configurations with runtime inputs to highlight residual risks.

Frequently Asked Questions

Does Mutual TLS prevent Server Side Template Injection in Hapi?
No. Mutual TLS authenticates clients but does not sanitize input. If certificate-derived data is passed directly into templates without escaping, SSTI can still occur. Always validate and escape user-influenced content regardless of transport-layer authentication.
How does middleBrick detect SSTI risks in Mutual TLS environments?
middleBrick scans unauthenticated attack surfaces and, where OpenAPI specs define mutual TLS security requirements, correlates them with runtime behavior. It flags routes that accept authenticated inputs (including certificate fields) and forward them to templates without proper escaping or validation.