HIGH command injectionhapijwt tokens

Command Injection in Hapi with Jwt Tokens

Command Injection in Hapi with Jwt Tokens — how this specific combination creates or exposes the vulnerability

Command Injection occurs when an API passes untrusted input directly to a system shell or process builder. In Hapi, this typically arises in routes that invoke external utilities (for example, image conversion, archive extraction, or report generation) and embed data from the request into shell commands. When JWT tokens are used for authentication, developers may inadvertently place token data into those commands—either by parsing the token on the server to extract a role or tenant identifier, or by forwarding claims from the token into dynamic parameters.

If the application builds a command string using values derived from the JWT (such as a username, scope, or custom claim) without strict validation and escaping, an attacker who can influence the token payload (forged token, compromised client, or confused deputy) can inject shell metacharacters. For example, a claim like sub or a custom role claim could contain ;, &, or backticks. When these values are interpolated into commands executed via child processes, the attacker can run arbitrary code on the host, bypassing intended access controls encoded in the token itself.

Because middleBrick scans the unauthenticated attack surface, it does not need valid JWTs to detect risky patterns. It examines runtime behavior and OpenAPI specifications to identify endpoints that accept user-controlled data and interact with the host process. If the spec or implementation shows that data derived from authentication (including JWT claims) influences process invocation, middleBrick flags this as a high-severity finding and maps it to relevant attack patterns such as OS Command Injection under OWASP API Top 10.

Jwt Tokens-Specific Remediation in Hapi — concrete code fixes

Remediation focuses on never passing data derived from JWT tokens directly to shell commands. Instead, use structured, language-level APIs for external processes and rigorously validate or sanitize any data that originates from the token.

Principle: Avoid shell interpolation entirely

Use built-in process APIs that accept arguments as arrays, bypassing the shell. This neutralifies metacharacters that enable injection.

const Hapi = require('@hapi/hapi');
const { execFile } = require('child_process');
const jwt = require('jsonwebtoken);

const server = Hapi.server({ port: 4000, host: 'localhost' });

server.route({
  method: 'GET',
  path: '/report',
  options: {
    auth: {
      strategy: 'jwt',
      scope: ['read:reports']
    }
  },
  handler: (request, h) => {
    // request.auth.credentials contains the decoded JWT payload
    const claims = request.auth.credentials;
    // Validate and constrain values from JWT before use
    const allowedFormats = ['pdf', 'csv'];
    const format = request.query.format;
    if (!allowedFormats.includes(format)) {
      return h.response('Invalid format').code(400);
    }
    // Use execFile with explicit arguments; do not build a shell string
    return new Promise((resolve, reject) => {
      execFile(
        'convert',
        ['--input-format', 'json', '--output-format', format, '/tmp/data.json'],
        (error, stdout, stderr) => {
          if (error) {
            return reject(new Error('Report generation failed'));
          }
          resolve(stdout);
        }
      );
    });
  }
});

server.start();

Principle: Strict validation and allowlists

If you must derive values from JWT claims, constrain them tightly. Use allowlists for identifiers such as usernames or tenant IDs. Do not rely on blacklists or simple pattern matching.

// Example: validating a tenant identifier from a JWT claim before any use
function getTenantDirectory(tokenPayload) {
  const tenantId = tokenPayload.tenant_id;
  // Allow only alphanumeric and underscores, length limits
  if (!/^[a-zA-Z0-9_]{1,32}$/.test(tenantId)) {
    throw new Error('Invalid tenant identifier');
  }
  return `/data/${tenantId}`;
}

// Safe usage: no shell involved
server.route({
  method: 'GET',
  path: '/files',
  options: {
    auth: 'jwt',
    handler: (request, h) => {
      const dir = getTenantDirectory(request.auth.credentials);
      // Use platform APIs that do not invoke a shell
      return fs.promises.readdir(dir);
    }
  }
});

Principle: Secure token handling and verification

Ensure JWT verification is strict (use strong algorithms, validate issuer and audience) so that attackers cannot forge tokens to introduce malicious claims. Even with safe process invocation, a compromised token can lead to privilege escalation if the server trusts claims for access decisions.

jwt.verify(token, publicKey, {
  algorithms: ['RS256'],
  issuer: 'https://auth.example.com',
  audience: 'api.example.com'
}, (err, decoded) => {
  if (err) { throw err; }
  // decoded is trusted and validated
});

Related CWEs: inputValidation

CWE IDNameSeverity
CWE-20Improper Input Validation HIGH
CWE-22Path Traversal HIGH
CWE-74Injection CRITICAL
CWE-77Command Injection CRITICAL
CWE-78OS Command Injection CRITICAL
CWE-79Cross-site Scripting (XSS) HIGH
CWE-89SQL Injection CRITICAL
CWE-90LDAP Injection HIGH
CWE-91XML Injection HIGH
CWE-94Code Injection CRITICAL

Frequently Asked Questions

Does middleBrick require a valid JWT to detect command injection risks in Hapi APIs?
No. middleBrick tests the unauthenticated attack surface and analyzes the OpenAPI specification to identify endpoints where untrusted data could reach process invocation, regardless of whether a valid JWT is provided.
Can JWT claims alone trigger a Command Injection finding even if no shell is used?
If JWT claims are reflected in responses or logs without validation, that is a different issue (data exposure or integrity). Command Injection requires external process interaction; middleBrick flags the combination of user-influenced data and process invocation, even when JWTs are involved.