mediumWeb Security

Clickjacking Vulnerability Due to Missing Frame Protection

What Is This Vulnerability?

Clickjacking is an attack where a malicious page loads your application inside a transparent or disguised iframe and tricks users into clicking buttons they cannot see. Without frame protection headers, an attacker can overlay your authenticated pages and capture clicks intended for invisible UI elements like delete buttons, payment confirmations, or permission grants.

Why It Happens

Browsers allow any page to embed another page in an iframe by default. Unless the embedded page explicitly sends X-Frame-Options or a CSP frame-ancestors directive, the browser has no way to know the embedding is unauthorized. Many applications omit these headers because they are not required for basic functionality.

Example Code

Vulnerableattacker-page.html
<!-- Attacker's page: the victim's app loads in a transparent iframe -->
<html>
<body>
  <h1>Click here to win a prize!</h1>
  <iframe
    src="https://victim-app.com/settings/delete-account"
    style="opacity: 0; position: absolute; top: 0; left: 0;
           width: 100%; height: 100%; z-index: 10;"
  ></iframe>
</body>
</html>
Fixedmiddleware.ts
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";

export function middleware(request: NextRequest) {
  const response = NextResponse.next();

  response.headers.set("X-Frame-Options", "DENY");
  response.headers.set(
    "Content-Security-Policy",
    "frame-ancestors 'none'",
  );

  return response;
}

How Hackers Exploit It

An attacker creates an enticing page (fake contest, video player, login form) and loads your app in a hidden iframe positioned over clickable elements. When the victim clicks what appears to be a harmless button, they are actually clicking a button in your app. This can trigger account deletion, money transfers, or permission changes without the victim's knowledge.

How to Fix It

Set the X-Frame-Options header to DENY on all responses. For more granular control, use the CSP frame-ancestors directive (e.g., frame-ancestors 'self') to specify which domains may embed your site. Both headers should be set for backward compatibility with older browsers.

Frequently Asked Questions

What is the difference between X-Frame-Options and CSP frame-ancestors?
X-Frame-Options is the older header with three values: DENY, SAMEORIGIN, and ALLOW-FROM. CSP frame-ancestors is more flexible, supports multiple domains, and is the modern standard. Use both for compatibility with older browsers.
Does clickjacking work on mobile browsers?
Yes. Mobile browsers render iframes and support touch events, so clickjacking can be adapted for tap-based interactions. The same frame protection headers apply to mobile browsers.
Can JavaScript alone prevent clickjacking?
Frame-busting scripts (like checking if window.top === window.self) provide some protection, but they can be bypassed with the sandbox attribute on iframes. HTTP headers are the reliable defense because they are enforced by the browser before any JavaScript runs.

Related Security Topics

Check Your Code for This Vulnerability

Run a free scan to check if your site is affected by clickjacking vulnerability due to missing frame protection.