Clickjacking tricks a user into clicking or tapping a control on your page while they believe they are interacting with something else, so a real, authenticated action happens without informed consent. It is also called UI redressing, and its mobile form is tapjacking. The core defence is simple, a single response header, yet it is routinely missing from login, consent, settings and error pages. This guide explains how framing attacks and newer non-framing variants work, where protection usually breaks, and how to roll out frame-ancestors and X-Frame-Options without breaking legitimate embeds. It is for developers, platform engineers and application security testers. Header syntax for the whole security header set is in the Security Headers Guide; this page goes deeper on framing.
Key takeaways
- Send
Content-Security-Policy: frame-ancestors 'none'by default. OWASP recommends it unless a specific framing need exists, and ASVS 5.0 V3.4.6 requiresframe-ancestorson every response. - Keep
X-Frame-Optionsonly as a fallback. UseDENYorSAMEORIGIN;ALLOW-FROMis obsolete and modern browsers ignore the whole header when they see it. - Headers, not meta tags. Neither
frame-ancestorsnorX-Frame-Optionsworks in a<meta>element. - Framing headers do not stop every UI redress. DoubleClickjacking targets a top-level window, so high-risk actions also need server-side transaction authorisation.
- Frame-busting JavaScript is a legacy fallback. Sandboxed iframes, double framing and navigation cancelling defeat it.
How the attack works
MITRE CWE-1021, Improper Restriction of Rendered UI Layers or Frames, covers an application that does not restrict, or incorrectly restricts, frames or UI layers belonging to another application or domain. CAPEC-103 (Clickjacking) and CAPEC-222 (iFrame Overlay) describe the attack patterns, and CAPEC-506 covers tapjacking.
- Embed: an attacker-controlled page loads the target page in an
<iframe>(or<object>or<embed>). - Disguise: the frame is made invisible or covered with decoy content, so the user sees a game, a video player or a "continue" button.
- Align: a sensitive control on the hidden page, such as "Delete", "Allow" or "Confirm transfer", sits under the decoy.
- Click: the user clicks the decoy and the browser delivers the click to the real page, which acts with whatever session the browser holds.
Unlike CSRF, the request comes from your own page in its own origin, so anti-CSRF tokens on the form are present and valid. CSRF is a separate class with its own controls.
Variants
| Variant | Mechanism | Does a framing header stop it? | Additional control |
|---|---|---|---|
| Classic iframe overlay | Transparent cross-site frame under decoy content | Yes | None needed beyond headers |
| Nested (double) framing | Target framed inside an intermediate frame | Yes for frame-ancestors, which checks every ancestor; older SAMEORIGIN implementations checked only the top level (RFC 7034) | Prefer frame-ancestors |
| Same-site sibling framing | A different subdomain, same-site but different origin, frames the page | Yes, if the policy does not allow that origin | Avoid broad wildcards; SameSite cookies do not help |
| DoubleClickjacking | A sensitive control in another top-level window is activated during a double-click | No | Server-side transaction authorisation; controls disabled until prior interaction |
| Tapjacking (Android) | A malicious app overlays or obscures a control in another app | Not applicable | filterTouchesWhenObscured, setHideOverlayWindows(true) |
Where it shows up
- Consent and permission screens: OAuth "Allow" pages, browser permission prompts relayed by the app, connect-an-integration dialogs. A framed consent grant can hand a third party a token. See the Consent Phishing and Illicit OAuth Grants Guide.
- Account settings: email, recovery options, MFA disable, privacy and sharing toggles. CWE-1021 cites changed privacy settings in a social application as an example impact.
- Admin and device consoles: CWE-1021's observed examples include a hardware product's web interface (CVE-2017-5697) and a data-loss prevention product (CVE-2017-4015).
- Pages outside the main template: error pages, static pages served by a CDN, legacy subpaths and mobile variants. WSTG-CLNT-09 calls out mobile and accessibility versions as places where headers are missing.
- Behind proxies: WSTG and the OWASP cheat sheet both note that a proxy can strip the framing header and silently remove protection.
- Legitimately embeddable pages: widgets, payment fields and dashboards designed to be framed. These need a precise allow-list rather than no policy.
Impact
CWE-1021 lists gaining privileges or assuming identity, bypassing protection mechanisms and reading or modifying application data. Impact depends entirely on what the framed page lets an authenticated user do.
| Framed control | Consequence |
|---|---|
| OAuth or app consent "Allow" | A third-party application receives delegated access or a refresh token |
| Generate API key or add integration | A long-lived machine credential the user never meant to create |
| Disable MFA, change recovery email | Account takeover path. See the Account Takeover Prevention Guide. |
| Payment or transfer confirmation | Financial loss |
| Sharing, privacy or permission toggles | Data exposure |
The identity angle: consent screens and key-creation buttons are where a human click becomes a non-human identity. Treat those pages as the highest-priority framing targets.
Detection
- Header coverage scanning: crawl every route, including login, error, API documentation and static paths, and record the effective
frame-ancestorsandX-Frame-Optionsvalues. One-URL scans miss most gaps. - Framing test: WSTG-CLNT-09 describes loading the page in a test iframe from another origin; if it renders, it is likely vulnerable. Repeat for mobile and accessibility variants.
- CSP violation reports:
frame-ancestorsviolations reported through CSP reporting show which origins try to embed you, both partners you forgot and hostile sites. - Edge configuration drift: alert when a CDN, WAF or reverse proxy rule removes or overrides the header.
- Sensitive action telemetry: for consent grants and key creation, record
Sec-Fetch-Destwhere available and investigate requests fromiframedestinations to pages that should never be framed.
Metrics
- Percentage of HTML responses with an enforced
frame-ancestorsdirective (target: 100 percent). - Number of routes allowing framing, each with an owner and an explicit origin list.
- Responses still using
ALLOW-FROMor meta-tag framing controls (target: zero). - High-risk actions protected by server-side transaction authorisation, as a share of the inventory.
- Time to detect a header removed by an edge configuration change.
Prevention and hardening
Comparing the controls
| Control | Values | Strengths | Limits |
|---|---|---|---|
CSP frame-ancestors | 'none', 'self', host or scheme sources | Multiple allowed origins; checks every ancestor; covers frame, iframe, object and embed; Baseline since January 2018 per MDN | Header only; no default-src fallback, so it must be set explicitly; wildcard https://*.example.com matches subdomains but not the bare domain |
X-Frame-Options | DENY, SAMEORIGIN | Supported by older browsers | One value only; cannot list partners; RFC 7034 is Informational; ignored where frame-ancestors is enforced; ASVS calls it obsolete |
| SameSite cookies | Lax, Strict | Cross-site frames load without the session, so attacks needing login fail | Not a framing control; no help for same-site siblings or pages that need no login |
| Transaction authorisation | Re-authentication, one-time code, out-of-band approval | Works for DoubleClickjacking and any UI redress | Friction; reserve for high-risk actions. OWASP notes an extra confirmation button alone is not enough |
| Frame-busting script | Hide body until self === top is confirmed | Legacy browsers without header support | Defeated by sandbox, double framing, onbeforeunload prompts and 204 flushing |
Choosing a policy
- Never embedded (most pages):
frame-ancestors 'none'plusX-Frame-Options: DENY. - Embedded only by your own pages:
frame-ancestors 'self'plusX-Frame-Options: SAMEORIGIN. RememberSAMEORIGINin modern browsers requires every ancestor to share the origin. - Embedded by named partners:
frame-ancestorswith exact origins, for example'self' https://portal.partner.example. OmitX-Frame-Optionson these routes or browsers lacking CSP support will block the partner; accept that trade-off consciously. - Embeddable widgets on arbitrary sites: keep the widget free of sensitive actions; send state changes to a top-level window you control, protected with
frame-ancestors 'none'.
Small organisations get most of the benefit from one site-wide header set at the web server or CDN. Large estates should set the default centrally and let individual routes relax it through a reviewed exception list, so a new page is protected without anyone remembering to add a header.
Implementation sequence
- Inventory embeds: list every page legitimately framed, by which origin, from CSP reports and partner contracts.
- Set the default centrally: add
frame-ancestors 'none'andX-Frame-Options: DENYat the server, framework middleware or edge so every HTML response, including errors, carries them. - Test in report-only: deploy
Content-Security-Policy-Report-Onlywith theframe-ancestorspolicy first and review reports for legitimate parents. Keep an existingX-Frame-Optionsin place meanwhile. - Add exceptions: relax only listed routes to exact origins; remove any
ALLOW-FROMand meta-tag attempts. - Enforce: switch to the enforcing header, then re-run the WSTG-CLNT-09 framing test across desktop, mobile and accessibility variants.
- Protect high-risk actions: add server-side transaction authorisation for consent, key creation, MFA changes and payments.
- Mobile apps: set
filterTouchesWhenObscuredon sensitive views and callWindow.setHideOverlayWindows(true)on Android 12 (API 31) and later; Android 12 also blocks untrusted overlay touches by default. See the Mobile App Security Guide. - Rollback: if a partner embed breaks, add its exact origin to that route's policy rather than removing the header site-wide.
Supplementary measures
- Frame-busting: if legacy clients matter, use the OWASP pattern that hides the page until it confirms it is top-level. OWASP does not recommend the simple
top.locationredirect script alone. - Confirmation dialogs: OWASP calls
window.confirm()supplementary confirmation, not a guarantee, because sandboxed frames withoutallow-modalssuppress dialogs; act only on atrueresult. - Disabled-until-interaction controls: a proposed DoubleClickjacking mitigation. Keep controls usable by keyboard, touch and assistive technology, as OWASP warns against designs that block some users.
Common failure patterns
- Header on the app, not the edge: error pages, redirects and CDN-served HTML skip the application middleware and go out unprotected.
- Meta-tag policies: teams add a
<meta>CSP or X-Frame-Options and believe they are protected; browsers ignore both for framing. - ALLOW-FROM left in place: OWASP warns it can fail open, leaving no clickjacking defence at all in modern browsers.
- Wildcard partner lists:
https://*.example.comlets any subdomain, including a forgotten or taken-over one, frame sensitive pages. - Assuming SameSite covers it: OWASP states SameSite does not block framing, and pages that need no login get no protection.
- Header only on the homepage: compliance scans check one URL while consent and settings pages lack the policy.
Framework mapping
| Framework | Reference | Relevance |
|---|---|---|
| OWASP ASVS 5.0 | V3.4.6; V3.3.2 | frame-ancestors on every response with X-Frame-Options treated as obsolete; SameSite set by purpose to limit UI redress |
| MITRE CWE / CAPEC | CWE-1021; CAPEC-103, CAPEC-222, CAPEC-506 | Weakness and attack patterns, including tapjacking |
| NIST SP 800-53 Rev. 5 | SA-11 Developer Testing and Evaluation; SC-23 Session Authenticity | Testing for UI redress; protecting session actions |
| NIST CSF 2.0 | PR.PS-06 | Secure software development practices |
| ISO/IEC 27001:2022 | A.8.28 Secure coding; A.8.29 Security testing in development and acceptance | Header baselines and testing |
| CIS Controls v8.1 | Control 16 Application Software Security | Secure configuration of web applications |
Practitioner checklist
- Send
frame-ancestors 'none'by default on every HTML response, including error and static pages. - Keep
X-Frame-Options: DENYorSAMEORIGINas a fallback where no partner framing is needed. - Remove
ALLOW-FROMand any meta-tag framing controls. - Maintain a reviewed list of routes that may be framed, with exact origins and owners.
- Roll out changes with
Content-Security-Policy-Report-Onlyand monitor violation reports. - Verify that CDNs, WAFs and proxies preserve the headers.
- Add server-side transaction authorisation to consent, key creation, MFA and payment actions.
- Set session cookies with explicit
SameSiteas defence in depth. - Protect sensitive Android views against overlays.
- Test desktop, mobile and accessibility variants with WSTG-CLNT-09.
Standards and references
- OWASP: Clickjacking Defense Cheat Sheet
- MITRE: CWE-1021 Improper Restriction of Rendered UI Layers or Frames
- OWASP: Application Security Verification Standard 5.0
- MDN: CSP frame-ancestors
- MDN: X-Frame-Options
- IETF: RFC 7034 HTTP Header Field X-Frame-Options
- OWASP: WSTG Testing for Clickjacking
- Android Developers: Tapjacking
Related NHI Mgmt Group resources: Security Headers Guide · Web Application Attacks Guide · Client-Side Security Guide · Web Application Security Testing Guide