MEDIUM clickjackingnestjscockroachdb

Clickjacking in Nestjs with Cockroachdb

Clickjacking in Nestjs with Cockroachdb — 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 embedded frame. In a NestJS application backed by CockroachDB, the vulnerability typically arises not from CockroachDB itself, but from missing HTTP response headers and insecure rendering logic that allow the app to be framed. When a NestJS app serves HTML or API responses without anti-clickjacking protections, and those responses reference data stored in CockroachDB (e.g., user-specific content rendered via templates or API-driven UI widgets), an attacker can embed the app in an <iframe> and manipulate user actions.

Consider a NestJS controller that fetches user profile data from CockroachDB and renders a page with sensitive actions such as changing email or payment settings:

@Controller('profile')
export class ProfileController {
  constructor(private profileService: ProfileService) {}

  @Get()
  async renderProfile(@Req() req) {
    const profile = await this.profileService.findByUserId(req.user.id);
    return { profile };
  }
}

If this endpoint returns HTML without a Content-Security-Policy (CSP) frame-ancestors directive or an X-Frame-Options header, and the UI includes state-changing forms (e.g., POST to update email), an attacker can load the page in an invisible iframe and coerce the user into submitting requests while logged into CockroachDB-backed session state. Because CockroachDB is often used in distributed, high-availability setups, the same NestJS app may serve requests from multiple regions; clickjacking risk remains consistent as long as the headers and CSP rules are absent, regardless of the database topology.

Another scenario involves API-driven NestJS frontend rendering (e.g., Server-Side Rendering or API calls from a frontend framework) that pulls sensitive UI state from CockroachDB. If the API endpoints lack proper authentication and authorization checks (BOLA/IDOR), and responses are embeddable, an attacker can craft a malicious page that embeds the API responses and uses CSS/JS to overlay invisible controls. The NestJS app’s reliance on CockroachDB for session or user data does not inherently cause clickjacking, but without CSP frame-ancestors and X-Frame-Options, the app remains susceptible when rendering views that perform privileged actions.

Cockroachdb-Specific Remediation in Nestjs — concrete code fixes

Remediation focuses on HTTP headers and secure rendering practices in NestJS, independent of CockroachDB’s storage mechanics. The following patterns assume you use NestJS controllers and services that may query CockroachDB via an ORM or raw client.

1. Set X-Frame-Options header

Prevent embedding of sensitive pages by adding the X-Frame-Options header. In NestJS, use an interceptor:

@Injectable()
export class XFrameOptionsInterceptor implements NestInterceptor {
  intercept(context: ExecutionContext, next: CallHandler) {
    const ctx = context.switchToHttp();
    const response = ctx.getResponse();
    response.setHeader('X-Frame-Options', 'DENY');
    return next.handle();
  }
}

// app.controller.ts
@Controller()
@UseInterceptors(XFrameOptionsInterceptor)
export class AppController {
  constructor(private profileService: ProfileService) {}
  // routes
}

2. Enforce Content-Security-Policy frame-ancestors

Use CSP frame-ancestors to control which origins can embed your pages. For maximum safety, disallow all framing:

@Injectable()
export class SecurityHeadersInterceptor implements NestInterceptor {
  intercept(context: ExecutionContext, next: CallHandler) {
    const ctx = context.switchToHttp();
    const response = ctx.getResponse();
    response.setHeader(
      'Content-Security-Policy',
      "default-src 'self'; frame-ancestors 'none'; script-src 'self'; style-src 'self';",
    );
    return next.handle();
  }
}

If you need to allow specific trusted domains (e.g., a dashboard), replace 'none' with a space-separated list of URLs:

response.setHeader(
  'Content-Security-Policy',
  "default-src 'self'; frame-ancestors 'self' https://trusted.example.com; script-src 'self'; style-src 'self';",
)

3. Secure forms and state-changing requests

Ensure POST/PUT/DELETE endpoints validate the Origin or Referer header as an additional defense-in-depth. In NestJS, create a guard or middleware:

@Injectable()
export class CsrfGuard implements CanActivate {
  canActivate(context: ExecutionContext) {
    const request = context.switchToHttp().getRequest();
    const origin = request.headers.origin;
    const referer = request.headers.referer;
    const allowedOrigin = 'https://yourdomain.com';
    if (!origin || origin !== allowedOrigin) {
      throw new ForbiddenException('Invalid origin');
    }
    return true;
  }
}

// Usage on state-changing routes
@Controller('profile')
export class ProfileController {
  constructor(private profileService: ProfileService) {}

  @UseGuards(CsrfGuard)
  @Post('update-email')
  async updateEmail(@Body() dto: UpdateEmailDto, @Req() req) {
    return this.profileService.updateEmail(req.user.id, dto.email);
  }
}

4. CockroachDB query safety

When your NestJS service queries CockroachDB, use parameterized queries to avoid injection that could indirectly affect rendering logic. Example using the CockroachDB Node.js driver:

const { Client } = require('pg'); // CockroachDB wire compatible
const client = new Client({
  connectionString: process.env.DATABASE_URL,
});
await client.connect();

// Safe parameterized query
const res = await client.query('SELECT id, name FROM users WHERE id = $1', [userId]);
await client.end();

Ensure that any dynamic values used in SQL are passed as parameters, not interpolated, to prevent data leaks that could be exploited in clickjacking scenarios (e.g., leaking CSRF tokens via SQL-injected UI).

Frequently Asked Questions

Does using CockroachDB change the clickjacking risk compared to other databases?
No. Clickjacking is a client-side UI issue driven by missing HTTP headers and insecure framing rules. The choice of database (including CockroachDB) does not affect whether an app can be embedded in an iframe; remediation is the same: set X-Frame-Options and a strict Content-Security-Policy frame-ancestors directive in NestJS.
Should I implement CSP frame-ancestors in addition to X-Frame-Options?
Yes. X-Frame-Options provides broad compatibility, but CSP frame-ancestors is more flexible and is the modern standard. Use both in NestJS for defense-in-depth: X-Frame-Options set to DENY or SAMEORIGIN, and CSP frame-ancestors set to 'none' or a controlled list of origins.