HIGH server side template injectionhapicockroachdb

Server Side Template Injection in Hapi with Cockroachdb

Server Side Template Injection in Hapi with Cockroachdb — how this specific combination creates or exposes the vulnerability

Server Side Template Injection (SSTI) in a Hapi application that uses CockroachDB can occur when user-controlled data is embedded into server-side templates without proper escaping or validation, and those templates are processed by a vulnerable template engine. Hapi does not include a built-in template engine, but it is commonly paired with engines such as Handlebars, Pug, or EJS. If a developer passes dynamic values from a CockroachDB query directly into template context objects and then renders user-supplied input within the template, an attacker can inject template logic that executes on the server.

Consider a route that fetches a user profile by username from CockroachDB and renders it with a template. If the username is taken from query parameters and placed into the template context unsanitized, and the template engine supports arbitrary code execution, an attacker can supply a payload that reads filesystem files or makes network calls. For example, in an EJS template, <% %> blocks allow JavaScript execution. A crafted input like <% require('fs').readFileSync('/etc/passwd') %> could disclose sensitive files if the template is rendered without escaping. CockroachDB does not introduce the injection by itself, but if query results are used to populate template variables without sanitization, the database becomes a source of data that may be rendered in a dangerous context.

The risk is compounded when route handlers construct template context from multiple database fields, including user-generated content stored in CockroachDB, and then pass the merged object to the view engine. An attacker may not directly control database rows, but they can influence data through other vectors (such as registration or profile update endpoints), leading to stored injection scenarios. Because SSTI exploits trust in the template layer, the database merely supplies content that appears benign but becomes malicious once rendered. The combination of Hapi’s routing layer, CockroachDB as a data store, and a permissive template engine creates a chain where untrusted data flows from storage to execution context.

To detect such issues, scans should examine how data moves from CockroachDB query results into template variables and whether user input is ever placed into template code blocks. Findings often highlight missing output encoding and overly broad context objects. Remediation focuses on strict input validation, using template engines with auto-escaping by default, and ensuring that only intended, sanitized data reaches the rendering layer.

Cockroachdb-Specific Remediation in Hapi — concrete code fixes

To prevent SSTI when using CockroachDB with Hapi, ensure that data retrieved from the database is never directly interpolated into templates. Instead, use template engines with built-in auto-escaping and strict context isolation. Below are concrete code examples for safer patterns.

Example 1: Using Handlebars with strict context and no eval

Handlebars does not execute arbitrary JavaScript by default, which reduces SSTI risk. Configure Hapi to use Handlebars and avoid registering helpers that enable code execution.

const Hapi = require('@hapi/hapi');
const Vision = require('@hapi/vision');
const Handlebars = require('handlebars');

const init = async () => {
  const server = Hapi.server({ port: 3000, host: 'localhost' });
  await server.register(Vision);

  server.views({
    engines: {
      html: {
        module: require('handlebars'),
        options: {
          // Disable compiler features that allow arbitrary code execution
          allowProtoAccessByDefault: false,
          allowProtoPropertiesByDefault: false,
          strict: { mode: 'safe' },
        },
      },
    },
    path: __dirname + '/views',
    layout: 'layout',
  });

  server.route({
    method: 'GET',
    path: '/profile/{username}',
    async handler(request, h) {
      const username = request.params.username;
      // Validate and sanitize input before using it in a query
      if (!/^[a-zA-Z0-9_]{3,30}$/.test(username)) {
        return h.response({ error: 'Invalid username' }).code(400);
      }

      const client = require('pg').Client;
      const dbClient = new client({
        connectionString: 'postgresql://user:password@cockroachdb-host:26257/dbname',
      });
      await dbClient.connect();
      const res = await dbClient.query('SELECT id, display_name FROM profiles WHERE username = $1', [username]);
      await dbClient.end();

      if (res.rows.length === 0) {
        return h.response({ error: 'Not found' }).code(404);
      }

      // Pass only safe, primitive data to the template
      const safeData = {
        profile: {
          id: res.rows[0].id,
          displayName: res.rows[0].display_name,
        },
      };

      return h.view('profile', safeData);
    },
  });

  await server.start();
  console.log('Server running on %s', server.info.uri);
};

init().catch((err) => {
  console.error(err);
});

Example 2: Using EJS with explicit escaping and no new Function

EJS can be safe if you disable evaluation of raw code and always escape output. Avoid <% %> for untrusted data and use <%- %> only for trusted content.

const Hapi = require('@hapi/hapi');
const Vision = require('@hapi/vision');
const ejs = require('ejs');

const init = async () => {
  const server = Hapi.server({ port: 3000, host: 'localhost' });
  await server.register(Vision);

  server.views({
    engines: {
      ejs: {
        compile: (source, options) => {
          // Use ejs.compile with minimal options, avoid custom functions in templates
          return ejs.compile(source, { ...options, async: false, client: true });
        },
      },
    },
    path: __dirname + '/views',
    layout: false,
  });

  server.route({
    method: 'GET',
    path: '/search',
    async handler(request, h) {
      const query = request.query.q;
      if (typeof query !== 'string' || query.length > 100) {
        return h.response({ error: 'Invalid query' }).code(400);
      }

      const client = require('pg').Client;
      const dbClient = new client({
        connectionString: 'postgresql://user:password@cockroachdb-host:26257/dbname',
      });
      await dbClient.connect();
      const res = await dbClient.query('SELECT title, snippet FROM items WHERE title ILIKE $1', [`%${query}%`]);
      await dbClient.end();

      // Only pass data, not code or raw user templates
      const items = res.rows.map((row) => ({
        title: row.title,
        snippet: row.snippet,
      }));

      // Render with EJS, ensuring output is escaped by default in the template
      return h.view('search', { items });
    },
  });

  await server.start();
  console.log('Server running on %s', server.info.uri);
};

init().catch((err) => console.error(err));

General remediation practices

  • Validate and sanitize all inputs that originate from URLs, headers, or payloads before using them in database queries or templates.
  • Use parameterized queries with CockroachDB (e.g., $1, $2 placeholders) to prevent injection at the database layer; this does not prevent SSTI but reduces overall risk.
  • Prefer template engines with auto-escaping and avoid dynamic evaluation within templates.
  • Limit the data passed to templates to only what is necessary, and avoid merging raw user input into template context objects.
  • Regularly scan your API with tools that include SSTI checks to catch misconfigurations early.

Frequently Asked Questions

Can SSTI via Hapi and Cockroachdb lead to remote code execution?
Yes, if a server-side template engine evaluates user-controlled data as code and the application uses CockroachDB to supply template variables containing attacker-influenced content, remote code execution can occur through injected template syntax.
Does middleBrick detect SSTI in Hapi applications using Cockroachdb?
middleBrick runs checks for Server Side Template Injection as part of its 12 security scans. It analyzes how data flows from sources like database query results into template rendering and reports findings with severity and remediation guidance.