Skip to content
ClickJack Test

← Blog

DoubleClickjacking: The Attack That Bypasses X-Frame-Options, CSP, and SameSite

Published June 2026 · Updated October 2026 · sourced from Paulos Yibelo's disclosure (Dec 2024), The Hacker News, and OWASP

Every mainstream clickjacking defense works the same way: it stops your page from being loaded inside someone else's iframe. X-Frame-Options, the CSP frame-ancestors directive, and SameSite cookies all assume the attacker needs to embed your site to deceive the user.

DoubleClickjacking, published by Paulos Yibelo in late December 2024 and widely covered from January 2025, embeds nothing. It swaps the window the user is looking at between the two clicks of a double-click, using window.opener.location. Because there is no iframe, none of the framing headers apply.

How it works

The sequence from Yibelo's write-up:

  1. The attacker hosts a page with a button that opens a new popup window.
  2. The popup opens on top (Yibelo notes popups can cover the whole browser) and prompts the user to double-click something innocuous: a fake captcha, a “verify you are human” widget, a cookie banner.
  3. While the user is about to double-click, the popup uses window.opener.location to navigate the original (parent) window to a sensitive target page, such as an OAuth authorization screen, a payment confirmation, or an account settings page. The popup still covers it, so the user sees nothing change.
  4. On the first click (the mousedown), the popup closes itself.
  5. The second click of the double-click lands on the now-exposed sensitive button in the parent window, under the cursor.
  6. The user has authorized the attacker's OAuth app, confirmed a payment, or toggled a security setting without ever knowing the window changed.

Why it bypasses existing protections

  • X-Frame-Options: irrelevant. No iframe is used, so there is nothing for it to block.
  • CSP frame-ancestors: irrelevant for the same reason. There is no framing relationship to deny.
  • SameSite cookies: this is the key insight. The attack runs in a separate popup window and navigates the parent as a top-level document, so the target page loads with its cookies. SameSite=Lax (the default in some browsers, including Chrome) permits cookies on top-level navigations, so the session is fully authenticated.

The attack exploits the timing gap between mousedown and click, not iframe transparency. That is a property of the input model, which framing headers were never designed to address.

Real impact

  • OAuth account takeovers on, in Yibelo's words, “almost every site that supports OAuth.”
  • One-click account changes: disabling security settings, deleting accounts, authorizing access, confirming transfers.
  • Browser extensions: Yibelo reports proofs of concept against crypto wallets (authorizing Web3 transactions) and a VPN extension (disabling it to expose the user's IP).
  • A mobile variant, “DoubleTap,” asks the target to double-tap instead.

Attacker proof-of-concept

A simplified version of the swap Yibelo describes. The popup redirects its opener to the target, then closes on the first mousedown. Illustration only:

// In the popup window (the fake captcha)
window.onmousedown = () => {
  // Send the parent window to the sensitive target page
  window.opener.location = "https://target.com/oauth/authorize?client_id=ATTACKER";
  // Remove ourselves so the second click lands on the target
  window.close();
};

By the time the second click lands, the popup is gone and the parent window's authorize button is under the cursor.

Mitigation: the Dropbox / Stripe / GitHub pattern

Yibelo's recommended client-side defense is to keep critical buttons disabled by default and re-enable them only after a gesture: mouse movement or a keyboard Tab. The idea is that the swapped-in page sees neither signal before the second click lands, so the button is still inert.

// Load with <script defer> so the controls exist when this runs.
// Disable critical controls until a real interaction is observed
if (window.matchMedia("(hover: hover)").matches) {
  document.querySelectorAll(".critical").forEach((el) => {
    el.disabled = true;
  });

  const enable = () => {
    document.querySelectorAll(".critical").forEach((el) => {
      el.disabled = false;
    });
  };

  // A genuine user moving the mouse or tabbing re-enables the buttons
  document.addEventListener("mousemove", enable);
  document.addEventListener("keydown", (e) => {
    if (e.key === "Tab") enable();
  });
}

Yibelo says Dropbox, Stripe, and GitHub use a similar script. The hover check means it skips touch devices, so it does not cover DoubleTap. It also only helps if the site ships it; a site that does nothing has no protection.

Treat it as a supplement. OWASP notes that mouse movement or a key press does not prove the user approved the action, and that controls must still work for keyboard, touch, and assistive technology. For high-risk actions, OWASP's primary recommendation is server-side transaction authorization, where the user reviews and confirms the specific details.

Long-term defense

Yibelo proposed a hypothetical Double-Click-Protection: strict response header, or an equivalent CSP extension, that would let browsers neutralize rapid window swaps without per-site JavaScript.

What has changed since

  • No browser fix that we could find. As of October 2026, we found no Chrome, Firefox, or Safari release note, intent to ship, or standards proposal for a DoubleClickjacking-specific defense, and no shipped Double-Click-Protection header. The site-side gesture check is still the main defense.
  • Cross-Origin-Opener-Policy on the target page does not stop the swap. The attacker's page starts the navigation, and a COOP same-origin target only lands in a new browsing context group (severing opener references) after it has loaded. The popup just needs to close itself. This is our reading of MDN's COOP documentation, not a tested guarantee.
  • Guidance caught up. The OWASP Clickjacking Defense Cheat Sheet now has a DoubleClickjacking section that makes server-side transaction authorization the primary defense.
  • Better proofs of concept. Jorian Woltjer published a more polished exploit (a fake Cloudflare captcha plus a Flappy Bird game), which Yibelo's post links as a follow-up.

What ClickJack Test detects, and what it does not

Our tool checks your X-Frame-Options and CSP frame-ancestors headers. Those headers stop classic, iframe-based clickjacking, which is the overwhelming majority of clickjacking seen in the wild.

No automated scanner can determine whether a site is vulnerable to DoubleClickjacking. It depends on how each sensitive button behaves and whether the site implements gesture-based protection: properties that live in application code, not in HTTP headers. The headers we check are still necessary, but they are not sufficient.

Test your site now →

Check your headers

See whether a site sends X-Frame-Options or CSP frame-ancestors, and what browsers will do with them.