HIGH command injectionnestjshmac signatures

Command Injection in Nestjs with Hmac Signatures

Command Injection in Nestjs with Hmac Signatures — how this specific combination creates or exposes the vulnerability

Command Injection occurs when an attacker can control part of a system command executed by the application. In NestJS, this risk can arise when you build an Hmac Signature verification flow that incorporates untrusted input into shell commands. For example, imagine an API endpoint that accepts a filePath parameter, computes an Hmac signature to validate a client-supplied signature, and then passes the file path to a native utility such as cat or tail via child_process. If the Hmac verification passes but the filePath is attacker-controlled and not properly sanitized, an attacker can inject shell metacharacters (e.g., ;, &, |, `, or $()) to execute arbitrary commands with the application’s privileges.

The combination of Hmac Signature integrity checks and dynamic command execution is dangerous because developers may assume that Hmac alone guarantees the safety of the data. Hmac ensures authenticity and integrity of the signed payload, but it does not constrain how that payload is used downstream. An attacker could supply a valid signature for a payload like file.txt; rm -rf / if the server concatenates user input into a shell command. This pattern often appears in integrations that call external binaries for file processing, log analysis, or format conversion. The vulnerability maps to the OWASP API Top 10 category Broken Object Level Authorization (BOLA) and Injection, and can lead to data exfiltration, service disruption, or full system compromise. Runtime scans performed by middleBrick can detect unsafe consumption patterns where untrusted data reaches external command execution, even when an Hmac check is present, because the scanner analyzes the unauthenticated attack surface and does not rely on authentication context.

Consider a NestJS controller that uses crypto.createHmac to verify a signature and then executes a shell command using execSync. If the verified payload includes user-controlled data used directly in the command string, the server is vulnerable. middleBrick’s LLM/AI Security checks and unsafe consumption tests are designed to surface these risks by correlating signature verification logic with command usage, highlighting where remediation is required to avoid injection despite valid Hmac verification.

Hmac Signatures-Specific Remediation in Nestjs — concrete code fixes

To mitigate Command Injection when using Hmac Signatures in NestJS, you must ensure that untrusted input never reaches a shell. The safest approach is to avoid shell execution entirely and use built-in Node.js APIs or well-maintained libraries for file and data operations. When shell interaction is unavoidable, rigorously validate and sanitize inputs, and use parameterized commands or allowlists.

Below are concrete code examples demonstrating secure practices.

Example 1: Hmac verification without shell execution

Verify the signature and process the file using Node.js filesystem APIs instead of shell commands:

import { Controller, Get, Query, BadRequestException } from '@nestjs/common';
import * as crypto from 'crypto';
import * as fs from 'fs';
import * as path from 'path';

@Controller('files')
export class FilesController {
  private readonly secret = process.env.HMAC_SECRET;

  @Get()
  getFile(@Query('file') file: string, @Query('signature') signature: string) {
    if (!file || !signature) {
      throw new BadRequestException('Missing file or signature');
    }
    const expected = crypto.createHmac('sha256', thissecret)
      .update(file)
      .digest('hex');
    if (!crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected))) {
      throw new BadRequestException('Invalid signature');
    }
    const safeBase = path.resolve('/var/data/uploads');
    const filePath = path.resolve(safeBase, file);
    if (!filePath.startsWith(safeBase)) {
      throw new BadRequestException('Invalid file path');
    }
    if (!fs.existsSync(filePath)) {
      throw new BadRequestException('File not found');
    }
    return fs.readFileSync(filePath);
  }
}

This approach validates the Hmac, ensures the resolved path is within an allowed directory, and reads the file using fs.readFileSync, avoiding any shell invocation.

Example 2: If shell execution is required, sanitize rigorously and avoid direct concatenation

If you must call an external binary, validate the input against a strict allowlist and use arguments rather than shell string concatenation:

import { Controller, Get, Query, BadRequestException } from '@nestjs/common';
import * as crypto from 'crypto';
import { execFile } from 'child_process';
import { promisify } from 'util';

const execFileAsync = promisify(execFile);

@Controller('convert')
export class ConvertController {
  private readonly secret = process.env.HMAC_SECRET;

  @Get()
  async convert(@Query('file') file: string, @Query('signature') signature: string) {
    if (!file || !signature) {
      throw new BadRequestException('Missing parameters');
    }
    const expected = crypto.createHmac('sha256', this.secret)
      .update(file)
      .digest('hex');
    if (!crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected))) {
      throw new BadRequestException('Invalid signature');
    }
    const allowedExtensions = ['.txt', '.csv'];
    const ext = file.slice(file.lastIndexOf('.'));
    if (!allowedExtensions.includes(ext)) {
      throw new BadRequestException('File type not allowed');
    }
    // Use execFile with an array of arguments; do not pass a shell string
    const { stdout } = await execFileAsync('cat', [file]);
    return stdout;
  }
}

Key remediation points: use execFile (not exec or shell forms), pass arguments as an array, validate file extension via an allowlist, and enforce Hmac verification before any processing. middleBrick’s CLI (middlebrick scan <url>) and GitHub Action can help identify places where user input reaches command execution so you can apply these fixes before deploying.

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 Hmac verification alone prevent Command Injection in NestJS APIs?
No. Hmac verification ensures the payload has not been tampered with, but it does not prevent an attacker from injecting shell metacharacters if that payload is later used in a shell command. You must avoid passing untrusted data to a shell or use strict allowlists and argument arrays.
How can I test my NestJS endpoints for Command Injection alongside Hmac Signature flows?
Use middleBrick’s CLI (middlebrick scan <url>) to scan unauthenticated endpoints. It runs checks including unsafe consumption and input validation, and it correlates findings with OpenAPI specs to highlight where user-controlled data reaches command execution despite Hmac checks.