What Is Clickjacking?
Clickjacking, also called a UI redress attack, works by loading a legitimate target page inside an invisible or heavily disguised iframe on an attacker-controlled page, then positioning it precisely over a decoy button the attacker wants the victim to click. The victim believes they are clicking 'Play Video' or 'Claim Prize' on the attacker's page, but their click actually lands on a real button in the invisible framed page underneath — such as 'Confirm Purchase' or 'Enable Camera Access'. Because the victim is genuinely logged into the target site in their own browser, the framed click carries their real session cookie, making the resulting action fully authentic from the server's perspective.
Cricket analogy: It's like a prankster placing an invisible replica of the real scorer's button panel exactly over a decoy 'refresh scores' button, so a fan trying to refresh the page actually presses 'confirm result' on the real, hidden panel underneath.
How Attackers Frame Your Site
The technical setup is straightforward CSS: an iframe pointing at the target site is given opacity: 0 or positioned off-screen except for the exact pixels covering the decoy button, using absolute positioning and a high z-index so it sits above the attacker's visible content while remaining invisible. Variants include cursorjacking, which manipulates the visual cursor position so what appears to be a safe click point is actually elsewhere, and likejacking, an older Facebook-era technique that tricked users into 'Liking' pages without realizing it. Because the framed page loads with the victim's real cookies and renders normally to the server, no authentication bypass is needed — the entire attack lives in deceiving the human, not the server.
Cricket analogy: It's like a broadcast overlay graphic positioned pixel-perfectly over the real umpire's signal, so viewers think they're seeing one call when the actual decision underneath was something else entirely.
<!-- Attacker's page: decoy button visible, real target invisible underneath -->
<style>
iframe {
position: absolute;
top: -400px; left: -50px;
width: 800px; height: 600px;
opacity: 0.0001; /* effectively invisible but still clickable */
z-index: 2;
}
.decoy-button {
position: absolute;
top: 250px; left: 300px;
z-index: 1;
}
</style>
<button class="decoy-button">Click to Claim Prize!</button>
<iframe src="https://target-site.example/account/delete"></iframe>
<!-- The invisible iframe's real 'Confirm Delete' button is aligned under the decoy -->Any page that performs a sensitive, one-click action — deleting an account, changing a setting, authorizing a payment, granting a permission — is a clickjacking target if it can be loaded in an iframe by an arbitrary third-party origin. The fix belongs on the target site, not the victim's browser habits.
Defending With Frame Options and CSP
The modern, correct defense is a server-sent HTTP header instructing the browser never to render the page inside a frame from another origin. The Content-Security-Policy directive frame-ancestors 'self' (or a specific allow-list of trusted origins) is the current standard and supports multiple origins and wildcards, superseding the older X-Frame-Options header, which only supports DENY, SAMEORIGIN, or a single ALLOW-FROM origin and lacks wildcard support. Both should ideally be set for defense in depth, since X-Frame-Options still has slightly broader legacy browser support. Client-side 'framebusting' JavaScript (checking if (top !== self) { top.location = self.location; }) is considered obsolete and unreliable, since it can be defeated by sandboxing the iframe or intercepting the navigation attempt — the header-based approach is enforced by the browser itself before rendering, which JavaScript running inside the frame cannot override.
Cricket analogy: frame-ancestors is like a stadium's entry policy explicitly listing which broadcaster vans are allowed to park and relay footage from inside the ground, rather than relying on the broadcaster to voluntarily behave.
Recommended header pair for modern browsers: Content-Security-Policy: frame-ancestors 'self'; plus X-Frame-Options: SAMEORIGIN as a legacy fallback. If your page must be embeddable by specific partners, list their exact origins in frame-ancestors rather than using a wildcard like *.
- Clickjacking overlays an invisible iframe of a real target page beneath a deceptive decoy button.
- The victim's real session cookie makes the resulting framed click fully authentic to the server.
- Common technique: CSS opacity:0 plus absolute positioning and z-index layering to align the invisible target precisely.
- Content-Security-Policy's frame-ancestors directive is the modern, standard defense.
- X-Frame-Options (DENY/SAMEORIGIN) should still be set alongside CSP for legacy browser support.
- Client-side framebusting JavaScript is obsolete and can be bypassed; server-sent headers are enforced by the browser before rendering.
- Any single-click sensitive action (delete, authorize, grant permission) is a potential clickjacking target if the page can be framed.
Practice what you learned
1. What is the core mechanism behind a clickjacking attack?
2. Why does a clickjacking attack succeed from the server's point of view?
3. What is the modern, recommended primary defense against clickjacking?
4. Why is client-side framebusting JavaScript considered an unreliable defense?
5. What does X-Frame-Options: SAMEORIGIN instruct the browser to do?
Was this page helpful?
You May Also Like
Security Headers Explained
HTTP security headers let a server instruct the browser to enforce protective behaviors, such as blocking mixed content, refusing to be framed, or restricting which scripts may execute, directly at the browser level.
CSRF Explained and Prevention
Cross-Site Request Forgery tricks a logged-in user's browser into submitting an unwanted, state-changing request to a site it trusts, using the victim's own valid session.
Security Misconfiguration
Security misconfiguration covers the gap between a system's secure potential and its insecure default or actual state — from exposed debug consoles to unpatched software and unnecessary open ports.