HTTP Security Headers

Defending Against Clickjacking with X-Frame-Options

Clickjacking, also known as a “UI Redress Attack”, is a malicious technique where an attacker tricks a user into clicking on a button or link on another page, when the user actually intended to click on the overlay page of a trusted website.

Implementing the X-Frame-Options header and CSP frame-ancestors directive is the primary defense against clickjacking.

The Mechanics of a Clickjacking Attack

In a clickjacking attack, the attacker does the following:

  1. Creates an innocent-looking website (e.g. “Get Free Gift Cards”).
  2. Embeds your legitimate website (such as your login page or a bank transfer page) inside a transparent <iframe> overlaid directly on top of the fake site.
  3. Positions the transparent iframe so that a high-value button (like “Transfer Funds” or “Delete Account”) aligns exactly with an innocent button on the fake site (like “Click here to win!”).
  4. The user, thinking they are clicking the “Click here to win!” button, actually clicks the invisible button on your legitimate site inside the iframe.

If your site allows framing and the user is currently logged in (authenticated via cookies), the browser executes the action using the user’s active session.


Defensive Strategy 1: The X-Frame-Options Header

Historically, the X-Frame-Options HTTP response header was introduced to specify whether a browser should be allowed to render a page in an <iframe>, <frame>, <embed>, or <object>.

Header Configurations

The header supports three directives:

# Option 1: Completely prevent any framing of this page
X-Frame-Options: DENY

# Option 2: Only allow pages on the same domain to frame this page
X-Frame-Options: SAMEORIGIN

# Option 3: Only allow the specified origin to frame this page (Legacy, poorly supported)
X-Frame-Options: ALLOW-FROM https://trustedpartner.com
  • DENY: This is the recommended setting for almost all pages, especially login panels, payment flows, and settings dashboards.
  • SAMEORIGIN: Use this if you need to frame your own pages within other pages of the same website.

Defensive Strategy 2: CSP frame-ancestors (Modern Standard)

While X-Frame-Options is widely supported by legacy browsers, modern best practice dictates using the frame-ancestors directive in your Content Security Policy (CSP).

frame-ancestors is much more flexible because it supports multiple specific domains and wildcards, resolving the limitations of the old ALLOW-FROM directive:

# Block all framing (equivalent to DENY)
Content-Security-Policy: frame-ancestors 'none';

# Allow framing only by the same origin (equivalent to SAMEORIGIN)
Content-Security-Policy: frame-ancestors 'self';

# Allow framing by specific trusted external domains
Content-Security-Policy: frame-ancestors 'self' https://trustedpartner.com https://*.anotherpartner.com;

[!IMPORTANT] Precedence Rules: If both X-Frame-Options and Content-Security-Policy: frame-ancestors are present on a page, modern browsers will ignore X-Frame-Options in favor of the CSP frame-ancestors directive. However, you should still ship both headers to ensure legacy browsers (such as Internet Explorer) remain protected.

How to Configure Your Server

Nginx Configuration

# Prevent framing completely
add_header X-Frame-Options "DENY" always;
add_header Content-Security-Policy "frame-ancestors 'none';" always;

Apache Configuration

# Prevent framing completely
Header always set X-Frame-Options "DENY"
Header always set Content-Security-Policy "frame-ancestors 'none';"

Sources & Standards

How Vioro monitors this

Vioro audits your response headers to verify that X-Frame-Options (or CSP frame-ancestors) is present and correctly set to DENY or SAMEORIGIN, ensuring attackers cannot frame your login panels or checkout flows.