HIGH clickjackingnestjshmac signatures

Clickjacking in Nestjs with Hmac Signatures

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

Clickjacking is a client-side UI redress attack where an attacker tricks a user into interacting with a hidden or disguised element inside an invisible or misleading layer. In a NestJS application that uses Hmac Signatures for request authentication, introducing security headers incorrectly—or omitting frame-protection headers—can leave the app vulnerable even when the signature logic is sound.

Hmac Signatures typically protect the integrity and authenticity of requests by verifying a signature derived from selected headers, a timestamp, and a shared secret. This mechanism helps prevent tampering and replay, but it does not inherently prevent the response from being embedded in an attacker-controlled page via <iframe>, <object>, or <embed>. If a NestJS endpoint that validates Hmac Signatures returns responses without Content-Security-Policy frame-ancestors rules or X-Frame-Options, an attacker can host a page that frames the trusted endpoint. The user’s browser will load the framed content, and UI elements such as buttons or links can be positioned transparently over the attacker’s controls, causing unintended actions when the user interacts with what they believe is a different site.

The risk is compounded when the Hmac verification focuses solely on request headers and does not account for the possibility that a legitimate authenticated request can be coerced via a forged UI context. For example, an endpoint that processes fund transfers based on a verified Hmac Signature may still be invoked through a malicious frame if the response is not protected by frame-locking headers. Attackers can overlay invisible submit buttons on top of legitimate UI components, leveraging the user’s authenticated session to execute actions without explicit consent. Since the Hmac validation passes, the server treats the request as valid, making the combination of strong signature verification and absent frame protection a particularly dangerous scenario.

To determine whether a NestJS endpoint is exposed, security scans such as those performed by middleBrick evaluate the presence and correctness of frame-protection headers alongside the authentication mechanism. The tool checks whether Content-Security-Policy includes frame-ancestors or whether X-Frame-Options is set, and correlates this with the presence of Hmac Signature validation in request handling. This combined analysis reveals whether strong request integrity controls inadvertently create a false sense of security while the UI surface remains embeddable.

Hmac Signatures-Specific Remediation in Nestjs — concrete code fixes

Remediation requires both robust Hmac Signature verification and explicit framing controls. On the server side, NestJS middleware or guards should validate the Hmac Signature before processing the request, and responses must include headers that prevent framing. Below are concrete examples that demonstrate both aspects in a NestJS application.

Hmac Signature verification middleware

The following middleware validates an Hmac Signature present in a custom header (e.g., X-API-Signature) against a shared secret and a canonical string composed of selected headers, timestamp, and payload. It rejects requests with invalid signatures and includes security headers in the response.

import { Injectable, NestMiddleware } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';
import * as crypto from 'crypto';

@Injectable()
export class HmacValidationMiddleware implements NestMiddleware {
  private readonly sharedSecret = process.env.HMAC_SHARED_SECRET || 'change-this-to-a-secure-random-string';

  use(req: Request, res: Response, next: NextFunction) {
    // Exclude verification for preflight to avoid complexity; rely on framework handling
    if (req.method === 'OPTIONS') {
      return next();
    }

    const receivedSignature = req.header('X-API-Signature');
    const timestamp = req.header('X-Timestamp');
    const nonce = req.header('X-Nonce');

    if (!receivedSignature || !timestamp || !nonce) {
      res.set({
        'Content-Security-Policy': "frame-ancestors 'none'",
        'X-Frame-Options': 'DENY',
      });
      return res.status(400).send('Missing required headers');
    }

    // Canonical string: method + path + timestamp + nonce + body (or sorted keys for JSON)
    const canonical = `${req.method}\n${req.path}\n${timestamp}\n${nonce}\n${JSON.stringify(req.body)}`;
    const expected = crypto.createHmac('sha256', this.sharedSecret).update(canonical).digest('hex');

    // Constant-time comparison to avoid timing attacks
    const isValid = crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(receivedSignature));

    // Enforce frame protection on every response
    res.set({
      'Content-Security-Policy': "frame-ancestors 'none'",
      'X-Frame-Options': 'DENY',
      'X-Content-Type-Options': 'nosniff',
    });

    if (!isValid) {
      return res.status(401).send('Invalid signature');
    }
    next();
  }
}

Applying the middleware in a NestJS module

Register the middleware globally or on specific routes. The example applies it to all routes under /api, ensuring Hmac verification and frame protection are consistently enforced.

import { Module, MiddlewareConsumer, RequestMethod } from '@nestjs/common';
import { HmacValidationMiddleware } from './hmac-validation.middleware';

@Module({
  controllers: [],
  providers: [],
})
export class SecurityModule {
  constructor(consumer: MiddlewareConsumer) {
    consumer
      .apply(HmacValidationMiddleware)
      .forRoutes({ path: 'api', method: RequestMethod.ALL });
  }
}

Frontend defensive practices

While server-side headers are primary, frontends should avoid making state-changing requests via GET and should include a custom, attacker-uncontrollable header (e.g., X-Requested-With) to complement Hmac verification. This reduces the risk of accidental leakage via accidental GET requests or misconfigured endpoints.

Testing the mitigation

Use curl to confirm headers are present and framing is blocked:

curl -I https://api.example.com/api/resource \
  -H 'X-Timestamp: 1700000000' \
  -H 'X-Nonce: unique-value-123' \
  -H 'X-API-Signature: '

Expected response headers should include Content-Security-Policy: frame-ancestors 'none' and X-Frame-Options: DENY.

Frequently Asked Questions

Does Hmac Signature validation alone prevent clickjacking?
No. Hmac Signature validation ensures request integrity and authenticity, but it does not prevent the response from being embedded in an attacker’s page. You must add frame-protection headers such as Content-Security-Policy frame-ancestors or X-Frame-Options to mitigate clickjacking.
Can CSP frame-ancestors replace X-Frame-Options in Nestjs apps using Hmac Signatures?
CSP frame-ancestors is the modern standard and can replace X-Frame-Options in most cases. For broad compatibility, you may set both: Content-Security-Policy: frame-ancestors 'none'; and X-Frame-Options: DENY. Ensure Hmac validation remains intact and that these headers are applied on every response.