On this page
  1. How does clickjacking work?
  2. What can an attacker make a victim do?
  3. How do I fix it?
  4. Where AI coding assistants get this wrong
  5. Checklist
  6. FAQ
    1. Is frame-busting JavaScript a valid defense?
    2. How do I test if my site is vulnerable to clickjacking?
  7. Related topics
  8. Sources
concept

What Is Clickjacking (and How Do You Prevent It)?

Clickjacking hides your page inside an invisible frame so clicks land on buttons the user can't see. Learn how it works and the one-header fix.

Quick answer

  • Clickjacking loads your page invisibly inside another page and tricks users into clicking buttons they can’t see.
  • It’s fixed with a single response header that forbids framing: X-Frame-Options or Content-Security-Policy frame-ancestors.
  • Test it by checking whether your site renders inside an iframe on another domain.

How does clickjacking work?

The attacker builds a page that contains your site in an invisible iframe — scaled, positioned, and made transparent — then places decoy buttons on top aligned with your real buttons. The user sees the decoy page and clicks ‘Win a prize’, but the click actually lands on ‘Confirm payment’ or ‘Approve’ inside the hidden frame. The attack needs no code on your site; it works entirely from the attacker’s page if your site allows being framed.

What can an attacker make a victim do?

Anything a single click can do: authorize a payment, grant an OAuth permission, approve a transaction, change a setting, follow an account, or submit a form with pre-filled values. Attacks are often layered with social engineering — the decoy page tells the victim to click several times, each click doing something on the hidden site. Actions that require only a click and no confirmation dialog are the prime targets.

How do I fix it?

Send a framing-prevention header on every page that shouldn’t be embedded. X-Frame-Options: DENY or SAMEORIGIN is the classic; Content-Security-Policy: frame-ancestors ‘none’ or ‘self’ is the modern replacement and is what security scanners recommend. If you genuinely need your pages embedded elsewhere (payment forms, widgets), allowlist exactly those origins in frame-ancestors. Note: X-Frame-Options is ignored if frame-ancestors is present, so set one consistently.

# nginx: prevent your site from being framed anywhere\nadd_header Content-Security-Policy "frame-ancestors 'none'" always;\nadd_header X-Frame-Options DENY always;

Where this bites vibecoders

The AI-generated app’s admin page — with a one-click ‘delete all data’ button — is clickjacking bait, and the assistant never adds frame headers unless asked. The fix is two lines in the server config or middleware, and the test is one browser command: check if your page renders inside an iframe from another origin. It’s the rare web vulnerability where the entire defense is a header, which makes it a pure ‘did the assistant remember it’ problem.

Where AI coding assistants get this wrong

  • No frame protection headers, leaving every page embeddable.
  • Adding X-Frame-Options but not frame-ancestors, or vice versa, and thinking both are set.
  • Setting frame-ancestors to allow all origins for one page that needs embedding, and applying it site-wide.
  • Relying on JavaScript frame-busting (if top != self), which attackers bypass trivially.

Checklist

  • Send frame-ancestors ‘none’ (or ‘self’) on all pages that don’t need embedding.
  • Add X-Frame-Options as a fallback for legacy browsers.
  • Allowlist only the exact origins that legitimately embed your pages.
  • Test by loading your page in an iframe from another origin and confirming it’s blocked.

FAQ

Is frame-busting JavaScript a valid defense?

No. Scripts like ‘if (top.location !== self.location) top.location = self.location’ can be bypassed with sandboxed iframes and other techniques. Header-based protection is enforced by the browser and can’t be bypassed from the attacker’s page — always use the headers.

How do I test if my site is vulnerable to clickjacking?

Create an HTML file on any domain that puts your site in an iframe and open it in a browser. If the page renders, you’re framable and need the headers. Security scanners also flag missing frame-ancestors automatically.

Sources

Share: