Overview
The X-Frame-Options HTTP response header instructs browsers whether a page may be rendered inside a <frame>, <iframe>, <embed>, or <object> element. When this header is missing — and no equivalent Content-Security-Policy: frame-ancestors directive is present — the page can be embedded by any third-party origin, enabling clickjacking (also called UI redress) attacks.
In a clickjacking attack, the adversary loads the victim page invisibly inside an iframe on a malicious site, then overlays deceptive UI elements to trick the user into clicking buttons or links on the hidden page without awareness.
How it works
The attack exploits the browser's default permissiveness toward cross-origin framing:
- Attacker constructs a malicious page that loads the target application in a transparent or zero-opacity
<iframe>positioned precisely over a convincing decoy UI. - User is lured to the malicious page via phishing, a malicious ad, or a compromised third-party site.
- User interacts with what appears to be the decoy interface (e.g., a "Play" button or a prize claim form), but the click is captured by the overlaid iframe and triggers an authenticated action on the target application (e.g., transferring funds, changing account email, granting OAuth permissions).
- Because the request originates from the user's authenticated session, server-side controls such as CSRF tokens may still be satisfied.
Key technical points:
X-Frame-Options: DENY— prevents all framing regardless of origin.X-Frame-Options: SAMEORIGIN— allows framing only by pages on the same origin.X-Frame-Options: ALLOW-FROM uri— allows a single specified origin (deprecated; inconsistently supported and superseded by CSP).- The modern, preferred mechanism is
Content-Security-Policy: frame-ancestors 'none'orframe-ancestors 'self', which offers finer-grained control and is defined by the W3C CSP Level 2 specification. - Frame-busting JavaScript (attempting to break out of iframes via
top.locationchecks) is an unreliable mitigation: it can be defeated by thesandboxattribute on the iframe or by disabling JavaScript. - Not all pages carry the same risk; pages that perform sensitive state-changing operations (authentication, account management, payments) are highest priority.
Business impact
Successful clickjacking exploits can result in:
- Unauthorised account actions — password changes, email address changes, two-factor authentication disablement, or deletion of account data performed silently on behalf of the victim.
- Financial fraud — initiating wire transfers, purchases, or subscription changes on banking and e-commerce platforms.
- Privilege escalation — tricking an administrator into approving elevated permissions or disabling security controls in an admin panel.
- Credential or token harvesting — combined with form injection techniques, attackers can harvest credentials submitted into overlaid forms.
- Regulatory exposure — under GDPR, PCI DSS, and similar frameworks, failure to implement basic security controls can constitute negligence and attract fines or audit findings.
- Reputational damage — user trust is eroded when accounts are compromised due to foreseeable, preventable weaknesses.
How to fix it
-
Set
X-Frame-Optionson all responses (minimum viable fix):
Add the header at the web server or application layer. UseDENYunless there is a specific need for same-origin framing:
or, if the application legitimately frames itself:X-Frame-Options: DENYX-Frame-Options: SAMEORIGIN -
Prefer
Content-Security-Policy: frame-ancestors(recommended):
This directive supersedesX-Frame-Optionsin all modern browsers, supports multiple trusted origins, and is the W3C-standardised approach:
orContent-Security-Policy: frame-ancestors 'none';
Deploying both headers provides defence in depth for older browsers that do not support CSP.Content-Security-Policy: frame-ancestors 'self' https://trusted-partner.example.com; -
Configure at the infrastructure layer:
Apply the header globally in reverse proxy or load balancer configuration (nginx, Apache, IIS, CDN edge rules) rather than relying solely on application code, to ensure consistent coverage including error pages and static assets.- nginx:
add_header X-Frame-Options "DENY" always; - Apache:
Header always set X-Frame-Options "DENY" - IIS: Add a custom HTTP response header via
web.configor the IIS Manager UI.
- nginx:
-
Audit legitimate framing requirements:
If the application must be embeddable (e.g., a widget or embedded payment form), enumerate all legitimate parent origins explicitly inframe-ancestorsrather than using a wildcard (*), which provides no protection. -
Include sensitive sub-pages:
Ensure the header is present not only on the home page but on all authenticated, state-changing endpoints including account settings, checkout flows, and admin interfaces. -
Test the fix:
Verify the header appears on HTTP responses using browser developer tools (Network tab) or a command-line check:curl -I https://example.com. Sensagraph continuously monitors for this header across your web properties and flags pages where it is absent.
References
- OWASP – Clickjacking Attack
- OWASP Cheat Sheet – Clickjacking Defense
- MITRE CWE-1021 – Improper Restriction of Rendered UI Layers or Frames
- RFC 7034 – HTTP Header Field X-Frame-Options (IETF)
- W3C CSP Level 2 – frame-ancestors Directive
- MDN Web Docs – X-Frame-Options
- MDN Web Docs – CSP: frame-ancestors