Clickjacking in Nestjs with Api Keys
Clickjacking in Nestjs with Api Keys — 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 embedded frame. In a NestJS application that relies on API keys for authorization, combining key-based authentication with pages that do not enforce frame protections can expose key-sensitive actions to clickjacking. For example, an endpoint that accepts an API key in an Authorization header or a custom header may still be invoked via a POST initiated from a malicious site if the browser sends credentials (cookies or authorization headers) automatically. If the response reveals sensitive information or performs a state-changing operation (e.g., rotating keys, changing scopes), embedding the endpoint in an iframe or overlaying transparent UI elements can lead to unauthorized key usage or account compromise. Even when API keys protect server-to-server calls, browser-based user sessions (cookies, session tokens) tied to administrative dashboards can be abused if X-Frame-Options or Content-Security-Policy frame-ancestors are not set. Attack patterns like those seen in OWASP API Top 10 A05:2023 Security Misconfiguration and A02:2023 Broken Authentication can manifest when NestJS routes do not explicitly guard against being framed. Without proper anti-clickjacking headers, an attacker can build a page that overlays a hidden form on the settings or key-rotation UI of your NestJS app, causing a logged-in user to inadvertently trigger key updates or data exports. Runtime scans by middleBrick can detect missing frame-protection headers across your endpoints, surface these exposures in its findings, and map them to compliance frameworks such as OWASP API Top 10 and SOC2. Note that middleBrick performs unauthenticated, black-box scanning and does not fix or block; it provides prioritized findings with remediation guidance so you can harden your NestJS routes.
Api Keys-Specific Remediation in Nestjs — concrete code fixes
Remediation focuses on two layers: protecting the browser interaction and securing how API keys are handled in NestJS. To defend against clickjacking, configure anti-frame headers on all responses that render UI or manage key-related operations. In NestJS, you can set HTTP headers globally or per route. Below are concrete examples that combine secure header policies with explicit API key handling.
1. Set anti-clickjacking headers globally
Use NestJS middleware to add Security HTTP headers. This ensures routes that manage or display API key settings cannot be embedded.
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 framing entirely
res.setHeader('X-Frame-Options', 'DENY');
// Modern replacement; restrict to same-origin if embedding is required
res.setHeader(
'Content-Security-Policy',
"frame-ancestors 'self' https://trusted.example.com",
);
next();
}
}
Register the middleware in your main application file:
import { NestFactory } from '@nestjs/core'; import { AppModule } from './app.module'; import { SecurityHeadersMiddleware } from './security-headers.middleware'; async function bootstrap() { const app = await NestFactory.create(AppModule); app.use(new SecurityHeadersMiddleware()); await app.listen(3000); } bootstrap();2. Explicit API key handling with secure headers and validation
Ensure API keys are transmitted via secure headers and not leaked in URLs or logs. Validate and scope keys server-side, and avoid reflecting keys in responses that could be captured by an attacker.
import { Controller, Post, Body, UseGuards, Header, HttpStatus, HttpCode } from '@nestjs/common'; import { ApiKeyGuard } from './api-key.guard'; interface RotateKeyBody { currentKey: string; newKey: string; } @Controller('keys') export class KeysController { constructor(private readonly keysService: KeysService) {} @Post('rotate') @UseGuards(ApiKeyGuard) @HttpCode(HttpStatus.OK) rotateKey(@Body() body: RotateKeyBody, @Header('x-api-key') key: string) { // Perform server-side validation and rotation return this.keysService.rotateKey(body.currentKey, body.newKey, key); } }Define a guard that ensures the key is present, properly scoped, and not reused in unsafe contexts:
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common'; import { Request } from 'express'; @Injectable() export class ApiKeyGuard implements CanActivate { canActivate(context: ExecutionContext): boolean { const request = context.switchToHttp().getRequest(); const key = request.headers['x-api-key']; // Validate key format, scope, and revocation status return typeof key === 'string' && key.length >= 32 && !key.includes(' '); } } Additionally, ensure responses do not inadvertently expose keys:
import { NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common'; import { Observable } from 'rxjs'; @Injectable() export class SanitizeHeadersInterceptor implements NestInterceptor { intercept(context: ExecutionContext, next: CallHandler): Observable{ const ctx = context.switchToHttp(); const response = ctx.getResponse (); // Remove sensitive headers from responses that may be rendered in browsers response.removeHeader('x-api-key'); return next.handle(); } } Combine these measures with CSP frame-ancestors and X-Frame-Options to ensure that even if an API key is accepted via header, browser-based clickjacking cannot trigger unauthorized key rotations or data exposure. middleBrick can validate that these headers are present and that key-related endpoints are not unnecessarily exposed in frames, surfacing misconfigurations in its reports.