MEDIUM clickjackingnestjstypescript

Clickjacking in Nestjs (Typescript)

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

Clickjacking is a client-side web security issue where an attacker tricks a user into interacting with a hidden or disguised UI element inside an iframe. In a NestJS + TypeScript application, the risk arises when HTTP response headers do not explicitly prevent the app from being embedded. NestJS does not set frame-protection headers by default, and if developers rely solely on frontend frameworks to enforce framing rules, the API can still be served in an attacker-controlled page.

Because NestJS often serves JSON APIs consumed by SPAs or server-rendered views, a response with a 200 status and no X-Frame-Options or Content-Security-Policy frame-ancestors directive can be loaded in an <iframe>. TypeScript’s static types do not enforce security headers; they only provide compile-time checks for JavaScript correctness. Therefore, the combination of NestJS routing/controllers and TypeScript’s compile-time safety can create a false sense of security, leaving endpoints clickjackable if headers are omitted.

Consider an endpoint that returns sensitive user data or an action-trforming form. If this endpoint is rendered inside an iframe on a malicious site, a user might unknowingly submit a form or click a button. The risk is higher when CORS is misconfigured to allow broader origins, and when the app serves pages in addition to APIs. MiddleBrick’s unauthenticated scan includes checks for missing frame-protection headers under its Data Exposure and Input Validation checks, which can surface this class of issue in a 5–15 second scan without credentials.

Real-world attack patterns mirror OWASP’s documented risks: an attacker hosts a page with <iframe src="https://your-nest-api.com/settings/delete-user"></iframe> and overlays invisible elements or social-engineered prompts to induce clicks. If the API lacks CSRF protections and frame-protection headers, the action may execute with the user’s authenticated session. This aligns with common findings mapped to OWASP API Top 10 and can be part of a compliance assessment for frameworks such as PCI-DSS and SOC2.

Typescript-Specific Remediation in Nestjs — concrete code fixes

Remediation focuses on setting HTTP headers at the NestJS middleware level and ensuring consistent behavior across all routes. In TypeScript, you can implement an interceptor or a NestJS middleware to add security headers to every response. Below are concrete, type-safe examples you can drop into a typical NestJS project.

1. Frame-protection via middleware (recommended)

Create a middleware that sets X-Frame-Options and Content-Security-Policy with frame-ancestors. This approach works for both API and server-rendered views.

// src/security/headers.middleware.ts
import { Injectable, NestMiddleware } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';

@Injectable()
export class SecurityHeadersMiddleware implements NestMiddleware {
  use(req: Request, res: Response, next: NextFunction) {
    // Prevent embedding in any frame
    res.setHeader('X-Frame-Options', 'DENY');
    // Modern replacement; DENY is stricter than SAMEORIGIN
    res.setHeader(
      'Content-Security-Policy',
      "frame-ancestors 'none'",
    );
    next();
  }
}

Register it in your module:

// src/app.module.ts
import { Module, MiddlewareConsumer, RequestMethod } from '@nestjs/common';
import { SecurityHeadersMiddleware } from './security/headers.middleware';

@Module({})
export class AppModule {
  constructor(private readonly consumer: MiddlewareConsumer) {}

  configure() {
    this.consumer
      .apply(SecurityHeadersMiddleware)
      .forRoutes({ path: '/*', method: RequestMethod.ALL });
  }
}

2. Apply CSP only where necessary

If you need limited framing (for example, embedding a dashboard), use a restrictive frame-ancestors list instead of 'none'. In TypeScript, keep values typed and avoid stringly-typed magic by defining constants.

// src/security/constants.ts
export const FRAME_ANCESTORS = ["'self'", 'https://trusted.example.com'];

// src/security/headers.middleware.ts (updated)
const csp = `frame-ancestors ${FRAME_ANCESTORS.join(' ')};`;
res.setHeader('Content-Security-Policy', csp);

3. Complementary protections

Combine headers with anti-CSRF tokens for state-changing requests and ensure CORS is locked down. MiddleBrick’s scans validate the presence of these headers and will surface missing frame-protection under its Data Exposure checks. If you use the Pro plan, continuous monitoring can alert you if a future deployment removes these headers.

For teams integrating into CI/CD, the GitHub Action can enforce that new deployments do not regress on these header checks, failing the build if risk scores drop below your chosen threshold. The CLI provides quick local verification: middlebrick scan <url> returns JSON with per-category findings, including Data Exposure.

Frequently Asked Questions

Does enabling CORS to allow specific origins mitigate clickjacking in NestJS?
No. CORS controls which origins can make cross-origin requests for resources like JSON, but it does not prevent a response from being rendered inside an iframe. To prevent clickjacking, you must set frame-protection headers such as X-Frame-Options or Content-Security-Policy with frame-ancestors.
Can TypeScript types alone prevent clickjacking vulnerabilities in NestJS?
No. TypeScript provides compile-time correctness for your code but does not affect runtime HTTP headers. Security headers must be set explicitly in your server configuration or via middleware; types cannot enforce that behavior.