BIPI
BIPI

Clickjacking Defence: Past X-Frame-Options

Cybersecurity

X-Frame-Options is legacy. CSP frame-ancestors is the modern answer, with edge cases for partner integrations and PDF viewers. Get the layering right or your clickjacking defence is theatre.

By Arjun Raghavan, Security & Systems Lead, BIPI · September 25, 2024 · 6 min read

#clickjacking#ui-security

A government services portal we audited had X-Frame-Options: SAMEORIGIN on every page. Compliance tick, audit pass, three years of clean reports. We framed their account deletion endpoint inside our test page on a different origin. It loaded. Turns out a CDN configuration change six months prior had stripped the header for a specific path, and nobody noticed because no automated test checked.

Clickjacking on account deletion isn't subtle. We demoed a UI where users 'clicked to claim a prize' and lost their accounts. The fix was straightforward. The lesson was that header-based defences need monitoring, not just configuration.

Why X-Frame-Options is legacy

X-Frame-Options was a hack: a single header with three valid values (DENY, SAMEORIGIN, ALLOW-FROM origin). ALLOW-FROM was deprecated because it didn't support multiple origins and had inconsistent browser support. So in practice teams used DENY or SAMEORIGIN, which is too coarse for any real-world business that needs partner embedding.

Content Security Policy's frame-ancestors directive replaces XFO with proper expressiveness: multiple origins, wildcards (with care), and deny-by-default semantics. All modern browsers support it. The reason XFO is still everywhere is inertia and audit checklists that haven't been updated.

What frame-ancestors looks like in practice

Default policy for most pages: 'frame-ancestors none' or 'frame-ancestors self'. Sets the don't-frame-me posture clearly. For pages that need to be embedded by specific partners, list them: 'frame-ancestors self https://partner1.com https://*.partner2.com'. Wildcards are a takeover risk if the partner has dangling subdomains, so audit before allowing them.

Both frame-ancestors and X-Frame-Options should ship together. If a browser supports CSP, frame-ancestors wins. If something old or weird hits XFO, you have a fallback. The cost of double-shipping is one extra header per response.

Edge cases that bite

  • PDF viewers: some browsers frame PDFs with their own viewer. frame-ancestors blocks this if not configured for the PDF route
  • Embedded checkout flows (Stripe, PayPal): your page may need to frame their domain, which is allowed by default; the issue is if their flow needs to frame you back
  • Mobile WebView: some apps frame their own login pages; frame-ancestors must permit the app's loader URL
  • Marketing iframes: third-party widgets that load your blog inline (rarely necessary, often misused)

Beyond the header

CSP frame-ancestors prevents framing entirely. But sensitive actions inside your own pages still need clickjacking-aware UX. Two-step confirmations on destructive actions (account delete, password change, money transfer). Rate limits that flag rapid-fire automated clicks. Re-authentication prompts for high-impact operations.

These are defence-in-depth. If a clever attacker finds a frame-ancestors bypass (browser bug, double-key entry through a service worker, etc), the second layer slows them enough that the first one gets patched before damage is done.

Operational checklist

  1. Audit every public-facing route for frame-ancestors and XFO
  2. Default to 'none' or 'self' unless there's a documented business reason
  3. Add automated tests that fail if either header is missing on critical paths
  4. Monitor CSP violations; clickjacking attempts often show up as frame-ancestors violations from unexpected origins
  5. Sensitive actions get explicit confirmation UI even with framing locked down

Clickjacking is a classic bug class with a clean modern fix. The reason it still ships is operational drift: headers added once, not monitored, lost in a CDN config change. The fix is automated testing, not better headers.

Read more field notes, explore our services, or get in touch at info@bipi.in. Privacy Policy · Terms.