← All fixes

Fix it

Missing clickjacking protection (X-Frame-Options) — how to fix it

Medium severityCWE-1021 (Improper Restriction of Rendered UI Layers)

Clickjacking is when an attacker loads your site inside an invisible frame on their own page and tricks a logged-in user into clicking something they cannot see, approving an action, changing a setting, confirming a payment. The defense is to tell browsers your site may not be framed by other origins, with X-Frame-Options or a Content-Security-Policy frame-ancestors directive.

Why it's a problem

Because the framed page is your real, authenticated site, the clicks land as genuine user actions. The attacker overlays their own bait over your buttons, so the user thinks they are clicking one thing while actually clicking another on your site. Any state-changing action, from settings to transfers, can be hijacked this way.

The pattern

// no framing protection — any site can embed yours
app.get("/", (req, res) => res.send(html));

The fix

// block framing by other origins
app.use((req, res, next) => {
  res.setHeader("X-Frame-Options", "DENY");
  res.setHeader(
    "Content-Security-Policy",
    "frame-ancestors 'none'"
  );
  next();
});

Why AI tools write this

Framing protection is a response header nothing in a feature prompt asks for, and the app works identically with or without it. So generated apps ship without it, leaving the clickjacking surface open by default.

The quick fix

  • Set X-Frame-Options: DENY (or SAMEORIGIN if you frame your own pages).
  • Add a Content-Security-Policy with frame-ancestors 'none' as the modern equivalent.
  • Apply these headers globally, at the framework or CDN level.

Want to know if this pattern is already in a repo you shipped? Scan a public repo free, no account needed.

Scan a repo →

Related: HTTP security headers

Clickjacking: Missing X-Frame-Options / frame-ancestors — The Fix: Prbl