Clickjacking
TL;DR: Target framed transparently under an attacker UI so clicks register on the target.
What it is
UI-redress attack: the target page is loaded inside an iframe on attacker.tld with opacity:0 or covered by misleading attacker UI. The victim sees attacker content and clicks what looks like “Play video”, but the click lands on a target-page button — “Delete account”, “Authorise OAuth scope”, “Transfer funds”. Same-origin policy does not block the framing, only cross-origin reads.
Preconditions / where it applies
- Target page does not send
X-Frame-Options: DENY/SAMEORIGIN, and CSP has noframe-ancestors - Target action requires only a single click / drag (no captcha, no re-auth)
- Victim authenticated to target while visiting attacker page
Technique
- Choose a one-click state-changing action on the target — typical examples: account delete, OAuth
Authorize, “add admin”, “approve payment”. - Build attacker page that frames the target and overlays bait UI:
1 2 3 4 5 6
<style> iframe { width:1000px; height:600px; opacity:0.0001; position:absolute; top:0; left:0; } #bait { position:absolute; top:240px; left:380px; } </style> <iframe src="https://target.tld/account/delete"></iframe> <button id="bait">Click to claim prize</button>
- Position the iframe so the target’s button aligns with the bait.
- Variants:
- Cursorjacking — fake cursor drawn via
cursor:none+ JS-tracked image; real cursor offset. - Drag-and-drop jacking — bait says “drag this code into the box”; victim drags attacker text into a target
contenteditableto inject content. - Likejacking / sharejacking — classic Facebook-era variant.
- Double-clickjacking (2024-2025) — first click closes a permission/auth prompt focus, second click lands on the now-focused OAuth approve button; bypasses many
X-Frame-Optionssetups because the second window is a popup, not an iframe.
- Cursorjacking — fake cursor drawn via
- Chain with csrf / oauth-flows to upgrade a click into a token grant.
Detection and defence
- Set
Content-Security-Policy: frame-ancestors 'none'(or'self') — modern, granular, supersedesX-Frame-Options. - Also set
X-Frame-Options: DENYfor legacy browsers. - Server-side require explicit confirmation (re-type password, captcha) before destructive actions — defeats one-click clickjack.
- Frame-busting JS is not sufficient (sandbox attribute defeats it); use headers.
- For double-clickjacking, require a user gesture inside the target window itself, or invalidate auth prompts on focus loss.
- Detect: hunt for pages with no
frame-ancestors/X-Frame-Optionsthat perform state changes; observe Referer-coming-from-attacker on sensitive endpoints. - Related: content-security-policy-bypass, csrf, postmessage-bugs.
References
- PortSwigger — clickjacking — labs and variants
- OWASP — clickjacking defence cheat sheet — header guidance
- Paulos Yibelo — double-clickjacking — 2024 variant