Skip to content
mediumCVSS 6.5CWE-1021A04:2021 – Insecure Design

Clickjacking-Assisted CSRF

Making the victim submit the real form with the real token — and why Lax-by-default has quietly killed most framed sessions.

Let the victim supply the token

Every defence in the token family rests on one assumption: the attacker cannot obtain a valid token for the victim's session. Clickjacking sidesteps it rather than breaking it — the victim's own browser renders the real form, with the real token, and the victim clicks the real button.

The server sees a perfectly legitimate request: correct token, correct Origin (the target's own, because the form is on the target's page), Sec-Fetch-Site: same-origin. Every header-based and token-based check passes, because from the server's point of view nothing is wrong.

The classic version frames the target invisibly and overlays bait so the victim's click lands on the target's button. In 2026 that version usually fails, for a reason that has nothing to do with clickjacking defences — see the next section. The techniques that still work route around the frame.

Why the framed version usually fails now

An <iframe> is a subresource, not a navigation. So the framed page gets the third-party cookie treatment:

CookieFramed page receives it?
SameSite=StrictNo
SameSite=LaxNo
No attributeNo — Chromium's Lax default, Firefox partitions, Safari blocks
SameSite=None; SecureChromium only

Read that column again: in every case except SameSite=None in Chromium, the framed page renders logged out. There is no session, so there is no pre-filled form, no valid token, and nothing worth clicking. The attack fails before any overlay is involved.

This is a genuine, if accidental, security improvement — cookie policy killed most clickjacking against authenticated actions as a side effect of killing third-party cookies. It also means a clickjacking finding against an authenticated action needs the cookie posture stated explicitly, or it is not credible.

The attack remains viable in three situations: the cookie is SameSite=None (in Chromium); the target uses non-cookie ambient auth like Basic or a client certificate; or the attacker uses a popup instead of a frame.

The classic framed overlay

HTMLfor reference — needs SameSite=NoneVulnerable
<!doctype html>
<html>
  <head>
    <style>
      /* The target, made invisible and positioned so the button the victim
         wants to click sits exactly under the bait. */
      #target {
        position: absolute; top: -412px; left: -85px;
        width: 800px; height: 900px;
        opacity: 0.0001;                 /* not 0 -- some detectors check for 0 */
        z-index: 2;
      }
      #bait {
        position: absolute; top: 300px; left: 200px;
        z-index: 1;
      }
    </style>
  </head>
  <body>
    <div id="bait">
      <h1>You have won a prize</h1>
      <button style="font-size:24px;padding:16px 32px">Claim it</button>
    </div>

    <!-- Only renders an authenticated page if the session cookie is
         SameSite=None; Secure, and then only in Chromium. Otherwise the
         victim's click lands on a logged-out page and nothing happens. -->
    <iframe id="target" src="https://app.example/account/delete"></iframe>
  </body>
</html>

Prevention

HTTPSecure
# The modern control. frame-ancestors supersedes X-Frame-Options and is the
# one browsers honour when both are present.
Content-Security-Policy: frame-ancestors 'none'

# Or, to allow framing by your own origin only:
Content-Security-Policy: frame-ancestors 'self'

# Or a specific partner, exact origin, no wildcards:
Content-Security-Policy: frame-ancestors https://partner.example.com

# Keep X-Frame-Options alongside it for older clients. It has no allowlist
# form worth using -- ALLOW-FROM was never widely supported and is obsolete.
X-Frame-Options: DENY

# Remember what these do NOT cover:
#   - Popups. window.open is not framing; neither header applies.
#   - Navigations. A top-level redirect to your page is not framing either.
# So framing headers are necessary but not sufficient for one-click actions.

Reporting

Clickjacking reports have a poor reputation with triage teams, largely because a great many are "the site is missing X-Frame-Options" with no demonstrated impact. To be taken seriously:

  • State the cookie posture. Quote the Set-Cookie and say why the framed page will be authenticated. If the session cookie is Lax or unset, a framed PoC will not reproduce and you should be using the popup variant instead.
  • Name the browser. A SameSite=None framed attack is Chromium-only in 2026.
  • Show a real action completing, not just that the page frames. Missing frame-ancestors on a page with no state-changing action is at most informational.
  • Record it. A screen recording of the click landing on the hidden control is far more convincing than a static screenshot, and takes a minute.
  • Explain the click. Why would the victim click there? A plausible bait flow is part of the finding; "assume the user clicks" is not.