Safeguard
Application Security

XS-Leaks explained: cross-site leak techniques and the defenses that stop them

XS-Leaks bypass the Same-Origin Policy without running a single line of attacker script — they read state through timing, frame counts, and error events instead.

Safeguard Research Team
Research
6 min read

Cross-site leaks, or XS-Leaks, are a vulnerability class that never needs to execute a single line of script inside the origin it's attacking. Instead of breaking the Same-Origin Policy directly, an XS-Leak reads observable side effects — how long a request takes, whether an image finishes loading, how many frames a page renders — to infer whether a logged-in user's search returned results, whether their account exists, or whether a specific record is visible to them. The technique category is documented in detail on xsleaks.dev, a community-maintained taxonomy and reference site, and in MDN's own "Cross-site leaks" guide. It matters because the fix isn't a single patch: it's a set of opt-in headers and cookie attributes that browser vendors shipped over roughly five years. One of those, Chrome's default flip of cookies to SameSite=Lax, landed with Chrome 80 on February 4, 2020, was paused on April 3, 2020 during early COVID-19 site-stability concerns, and didn't fully resume enforcement until July 14, 2020 — a five-month gap that shows how disruptive even a well-intentioned platform-wide defense can be. This post walks through how XS-Leaks actually work and which of the three major defenses — COOP, CORP, and SameSite — closes which door.

What makes an XS-Leak different from XSS or CSRF?

An XS-Leak differs from XSS because it never injects or executes attacker-controlled script in the victim origin, and it differs from CSRF because it doesn't need to change state — it only needs to observe it. A CSRF attack forges a request that performs an action (like transferring funds) using the victim's ambient cookies; an XS-Leak instead asks a yes/no question about the victim's state on another site and reads the answer through a side channel the platform exposes by design, such as whether a frame finished loading or how many <iframe> elements rendered inside it. This is why XS-Leaks are sometimes called "cross-site search" or "state inference" attacks in academic literature: a 2021 ACM CCS paper, "XSinator.com: From a Formal Model to the Automatic Evaluation of Cross-Site Leaks in Web Browsers" by researchers at Ruhr University Bochum and Niederrhein University of Applied Sciences, catalogued dozens of these leak techniques and tested them against every major browser, finding that most browsers were vulnerable to a majority of the known variants at the time.

How do frame-counting and error-event leaks actually work?

Frame-counting and error-event leaks work by turning a binary browser behavior into a readable bit of cross-origin state. In a frame-count leak, an attacker page opens a target URL — say, a search-results page — inside an <iframe> and counts how many nested frames the target renders; a results page with matches might render more UI chrome or ad frames than an empty-results page, and window.frames.length from the attacker's context is readable even across origins. In an error-event leak, the attacker embeds a resource from the target site as an <img> or <script> tag and listens for the onload versus onerror event: if a URL like target.com/user/12345 returns a 200 when the record exists and a 404 when it doesn't, the browser fires a different event depending purely on the victim's authenticated session — leaking a yes/no answer without the attacker ever reading the response body. Timing attacks follow the same logic using fetch()/XHR response latency instead of load events.

What does Cross-Origin-Opener-Policy (COOP) actually stop?

Cross-Origin-Opener-Policy stops a specific but common XS-Leak pattern: using a retained JavaScript reference to a popup or opened window to probe its state from the outside. Without COOP, if a victim's page opens attacker.com via window.open(), or the reverse, the two documents can share a browsing context group, and the opener can call things like targetWindow.frames.length or targetWindow.closed to infer navigation history or login state, and can even keep a reference alive to time postMessage responses. Setting the response header Cross-Origin-Opener-Policy: same-origin forces the page into its own browsing context group, severing that window reference regardless of how the two pages are linked. Per xsleaks.dev's own defense documentation, COOP is specifically effective against the "window reference" and popup-based leak family, and it's also one of the two headers (alongside Cross-Origin-Embedder-Policy) that Chrome and Firefox require before granting cross-origin-isolated access to higher-precision APIs like SharedArrayBuffer, tying it directly into Spectre-era process-isolation hardening.

What does Cross-Origin-Resource-Policy (CORP) protect that COOP doesn't?

Cross-Origin-Resource-Policy protects individual resources — images, scripts, fonts, JSON responses — from being loaded by other origins at all, which is a different boundary than the window-level isolation COOP provides. A server sets Cross-Origin-Resource-Policy: same-origin (or same-site, or the permissive cross-origin) on a response, and a compliant browser will refuse to let a cross-origin <img>, <script>, or fetch() load that resource, blocking it before any error/load-timing side channel can even fire. This closes off both classic XS-Leaks that rely on embedding a target resource to read its load behavior and Spectre-style attacks that rely on getting sensitive cross-origin data into the same process to read via speculative execution. Because CORP is opt-in per resource, xsleaks.dev's guidance treats it as complementary to COOP rather than a replacement — a site typically needs both to close the window-reference and resource-embedding leak families simultaneously, alongside Cross-Origin-Embedder-Policy (COEP) for full cross-origin isolation.

Why did SameSite cookies close so many XS-Leaks at once?

SameSite cookies closed a disproportionate share of XS-Leaks because so many of the leak techniques depend on the victim's session cookie being attached to the attacker-triggered request in the first place — no cookie, no personalized response, no observable difference to leak. Setting Set-Cookie: session=...; SameSite=Lax tells the browser to withhold that cookie on most cross-site requests (everything except top-level, same-site-safe navigations like clicking a link), so a request to target.com/search?q=admin triggered from attacker.com via an <img> or hidden <iframe> arrives logged out, returning the same generic response regardless of the real answer. Chrome made SameSite=Lax the default for any cookie set without an explicit SameSite attribute starting with Chrome 80, according to the Chromium project's own SameSite updates page — a platform-wide change that, per contemporaneous ghacks.net coverage, temporarily rolled back in April 2020 before full enforcement resumed that July. SameSite=Strict closes the gap further by withholding cookies even on top-level cross-site navigation, at the cost of breaking flows like clicking an emailed link into a logged-in page.

No single header closes every XS-Leak variant cataloged on xsleaks.dev — frame-counting, timing, and error-event leaks each have edge cases that survive one defense but not another, which is why COOP, CORP, and SameSite cookies are deployed together rather than as substitutes for each other.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.