Clickjacking Explained: How to Protect Your Website from Hidden HTML Frames

Clickjacking Explained: How to Protect Your Website from Hidden HTML Frames
Clickjacking Explained: How to Protect Your Website from Hidden HTML Frames

Discover how clickjacking attacks work and explore effective prevention methods like X-Frame-Options, Content Security Policy, and sandboxing in this detailed guide.

You click a button that says “Play.” Nothing seems to happen. But the click didn’t land on the button you saw. It landed on a “Confirm transfer” button belonging to your bank, loaded invisibly one layer above the page. You never saw it, and you just authorized it.

That’s clickjacking, sometimes called UI redressing. An attacker stacks a transparent frame of a real site on top of a decoy, so your clicks go somewhere you can’t see. In this guide we’ll walk through how the attack works, then how to stop it. The short version: one HTTP header does most of the job, and the header you probably heard about first is the older one.

Table of Contents

What Is Clickjacking?

Clickjacking is a web attack where you’re tricked into clicking something other than what you think you’re clicking. The usual trick: an attacker loads a real, trusted site inside a transparent or hidden iframe, then puts their own bait content underneath it. You interact with what looks legitimate, but your clicks pass through to the hidden frame.

It gets dangerous the moment a click does something real: submitting a form, moving money, flipping an account setting. You have no idea a second page is even there, which is exactly why it works.

How Does Clickjacking Work?

The mechanics are simple. The attacker embeds an invisible or nearly invisible iframe holding a site you’re already logged into. Then they position their visible content so that when you click a button or link you can see, you actually hit a control inside the hidden frame.

Here’s a stripped-down version:

Example: Basic Clickjacking Attack Using Hidden Iframes
HTML
<html>
<head>
    <title>Clickjacking Example</title>
    <style>
        .hidden-frame {
            position: absolute;
            top: 0;
            left: 0;
            opacity: 0;
            z-index: 9999;
            width: 100%;
            height: 100%;
        }
    </style>
</head>
<body>
    <h1>Welcome to the Game</h1>
    <button>Click to Start!</button>
    <iframe src="https://trusted-website.com" class="hidden-frame"></iframe>
</body>
</html>

The iframe is set to opacity: 0 and stretched over the whole page with a high z-index, so it sits on top of everything while staying invisible. The user aims for “Click to Start!” and the click lands on whatever’s under the cursor inside trusted-website.com instead.

Real-World Examples of Clickjacking

This isn’t theoretical. It’s shown up in the wild in a few recurring shapes:

  • Likejacking: a hidden Facebook “Like” button gets placed under bait, so users boost a page or post they never meant to touch.
  • Credential and action theft: a real login or “authorize app” flow is framed under an attacker’s interface, so what looks like a harmless click grants access on the real service.
  • Financial exploitation: a victim approves a payment or transfer by clicking what they thought was an unrelated button.
Basic Clickjacking Prevention Techniques

The whole attack depends on your site being loadable inside someone else’s frame. So the fix is to tell the browser who, if anyone, is allowed to frame you. That’s an HTTP response header, not something you can bolt on with page markup.

1. X-Frame-Options HTTP Header

X-Frame-Options is the older control. It’s blunt: it takes only two values worth using, and it still works in essentially every browser, which is why plenty of sites keep sending it. Modern guidance treats it as a legacy fallback behind CSP frame-ancestors (covered below), but there’s no harm in setting both.

Example: Denying All Framing
HTML
<!– HTML HTTP Header Example –>
X-Frame-Options: DENY

DENY means no one gets to frame your site, including you. It’s the strongest setting and the right default if you never legitimately embed your own pages.

Example: Allowing Only Same-Origin Framing
HTML
<!– HTML HTTP Header Example –>
X-Frame-Options: SAMEORIGIN

SAMEORIGIN lets pages on your own domain frame you, while blocking every other site. Use this if you rely on iframes internally. One thing to skip: the old ALLOW-FROM value is deprecated and modern browsers ignore it, so it gives you no protection. If you need to allow specific external sites, use CSP frame-ancestors instead.

2. Framebusting JavaScript

You’ll also see “framebusting” scripts recommended: JavaScript that checks whether the page is inside a frame and tries to break out. Be honest about what this is. It’s a fallback, not a real defense.

Example: Framebusting JavaScript
JS
/**
 * This JavaScript checks if the page is being framed.
 * If it is, the top-level window is redirected to the current page.
 */
if (window.top !== window.self) {
    window.top.location = window.self.location;
}

The script compares window.top with window.self, and if they differ it forces the top window to reload with your page. The problem is it can be defeated. A framing page can sandbox the iframe to block navigation, or the browser can be told to suppress it, and if the user has JavaScript disabled it does nothing at all. Rely on the headers. Treat framebusting as a belt-and-suspenders extra at most.

Advanced Clickjacking Prevention Techniques

The header below is the modern control. If you only do one thing, do this one.

1. Content Security Policy (CSP) Frame Ancestors Directive

The frame-ancestors directive in a Content Security Policy says exactly which origins may frame your content. It supersedes X-Frame-Options, handles the “allow a specific external site” case that ALLOW-FROM failed at, and where both headers are present, a supporting browser honors frame-ancestors.

Example: Allowing Specific Domains to Frame Your Content
HTML
<!– HTML HTTP Header Example –>
Content-Security-Policy: frame-ancestors 'self' https://trusted-site.com;

Here only your own origin and https://trusted-site.com can frame you. Everything else is blocked. Use frame-ancestors 'none' for the CSP equivalent of DENY.

2. Sandboxing Iframes

This one protects the other direction. When you’re the one embedding a third-party page, the iframe sandbox attribute strips the framed content down to only what you allow back in.

Example: Using the Sandbox Attribute
HTML
<!– HTML Iframe Example with Sandboxing –>
<iframe src="https://example.com" sandbox="allow-scripts"></iframe>

An empty sandbox denies almost everything; each token you add, like allow-scripts, grants one capability back. Forms, pop-ups, and top-level navigation stay off until you explicitly permit them.

Conclusion

Clickjacking works because your pages can be framed by anyone. Take that away and the attack has nothing to stand on. Send Content-Security-Policy: frame-ancestors as your primary control, keep X-Frame-Options alongside it for older browsers, and use the iframe sandbox attribute whenever you embed someone else’s content.

Skip the tricks that only look like security. ALLOW-FROM is dead, and framebusting JavaScript can be bypassed. Set the headers once, confirm they’re actually going out on your responses, and you’ve closed the door on this class of attack for both you and your users.

Leave a Comment

Your email address will not be published. Required fields are marked *


Scroll to Top