Back to Technical Mode
Clickjacking
An attacker overlays a transparent iframe of your site on top of their own deceptive page, positioning a sensitive button (like 'Delete Account' or 'Authorize Payment') exactly under something the victim is enticed to click — the victim's real click is captured by your site, not the attacker's.
The victim genuinely clicks on your real page — their session, their cookies, their authenticated click — they just don't realize which page they're clicking on because of the invisible overlay
This is entirely a browser-rendering-layer attack — no code on your site needs to be buggy for it to work, as long as your site can be framed at all
The fix is telling the browser your page must never be rendered inside a frame (or only inside frames from origins you trust) — content, not behavior, is what's being defended
The X-Frame-Options header is the older, simpler control; the frame-ancestors directive of Content-Security-Policy is the modern, more flexible replacement and takes precedence where both are supported
Want this flow explained in detail as it plays?
GET/account/delete
Headers
Host:app.flowground.dev
Body
Loading...
Quick-Insert Payloads
Defense in depth
- Send X-Frame-Options: DENY (or SAMEORIGIN if legitimate same-site framing is needed) on every response for pages that perform sensitive actions
- Set Content-Security-Policy: frame-ancestors 'none' (or a specific allowlist of trusted origins) as the modern, more expressive replacement/complement to X-Frame-Options
- For the highest-risk actions (account deletion, fund transfers), consider requiring a re-confirmation step (re-entering a password, a distinct confirmation dialog) that's harder to blindly click through even if framing somehow succeeds
Vulnerable vs. Fixed
app.get("/account/delete", requireAuth, (req, res) => {
res.render("confirm-delete"); // no framing protection headers set at all — can be embedded in any iframe
});