Skip to content

Add a guide on xs-leaks#38977

Merged
wbamberg merged 33 commits intomdn:mainfrom
wbamberg:cross-site-leaks
May 13, 2025
Merged

Add a guide on xs-leaks#38977
wbamberg merged 33 commits intomdn:mainfrom
wbamberg:cross-site-leaks

Conversation

@wbamberg
Copy link
Copy Markdown
Collaborator

@wbamberg wbamberg commented Apr 4, 2025

Here's a PR to add a guide to cross-site leaks. I feel like this might need some further work but it should be good enough for a review.

To understand the rationale for some of the choices I've made it would be worth looking at #3526 (comment) and the response.

XS-leaks are a bit different to the other attacks because they are very diverse. So I've chosen three quite different attacks, to try to give an idea of their diversity, and tried to explain how the attacks have similarities and how various defenses can help with them. I'm reasonably happy with the first two attacks - error events and frame counting - but wasn't sure which to pick for a third, and not sure the CSP one is a good choice.

I haven't talked about CORP since AFAICT CORP is mostly presented in connection with Spectre/Meltdown, which I haven't discussed here (again see #3526 (comment) for why). But perhaps I should after all introduce it, maybe as a defense against the error event attack?

I'm conscious as I write these guides to different that we're getting a lot of duplication from one guide to another in the "Defenses" section, outlining for example how to do framing protection, or set SameSite. I think we might want to factor these out into separate guides at some point.

I mentioned at the start that "side channels" can be a vector for xs-leaks, and think we should have a glossary page for that, but didn't write it yet.

Fixes #3526.

* upstream/main: (172 commits)
  chore: improve code style guide (mdn#38715)
  fix: typo on `Error.isError()` page (mdn#38754)
  plural consistency (mdn#38747)
  fix: auto-cleanup by bot (mdn#38695)
  Synchronize with BCD v5.7.4 (mdn#38709)
  Add docs for JS self-profiling API (mdn#37796)
  Better SameSite docs (mdn#38710)
  Added missing explanation for Array Literals (mdn#38745)
  Add a page on CSRF (mdn#38151)
  Fix description of several Range methods (mdn#38518)
  Remove extraneous span (mdn#38696)
  Add a definition for media containers, improve how the media files are defined and Remove wrong information (mdn#38721)
  Move visited selector guide to CSS selectors module (mdn#38642)
  Make JSON learning article more technically precise (mdn#38644)
  Make translate3d() interactive example code valid (mdn#38647)
  Clarity on Safari support for custom elements (mdn#38727)
  feat(css): Link to learning doc about text direction (mdn#38719)
  Fix typo (mdn#38739)
  move guide to module: inline formatting context (mdn#38637)
  Fix CSS pseudo-class lists (mdn#38576)
  ...
@github-actions github-actions bot added Content:Security Security docs size/m [PR only] 51-500 LoC changed labels Apr 4, 2025
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 4, 2025

Preview URLs

Flaws (4)

Note! 1 document with no flaws that don't need to be listed. 🎉

URL: /en-US/docs/Web/Security/Attacks/XS-Leaks
Title: Cross-site leaks (XS-Leaks)
Flaw count: 4

  • broken_links:
    • /en-US/docs/Web/HTTP/Headers/Set-Cookie#samesitesamesite-value is a redirect
    • /en-US/docs/Web/HTTP/CSP#clickjacking_protection is a redirect
    • /en-US/docs/Web/HTTP/CSP is a redirect
    • /en-US/docs/Web/HTTP/Headers/Content-Security-Policy/frame-ancestors#browser_compatibility is a redirect
External URLs (4)

URL: /en-US/docs/Web/Security/Attacks/XS-Leaks
Title: Cross-site leaks (XS-Leaks)

(comment last updated: 2025-05-13 21:34:20)

wbamberg added 4 commits April 6, 2025 11:09
* upstream/main: (158 commits)
  fix: remove unrelated add() method reference from openCursor() doc (mdn#38998)
  Fix link (mdn#38995)
  Remove uncovered topic from a tuturial overview. (mdn#38999)
  Change the description of Temporal.Instant (mdn#38996)
  Remove `port` mention for URL `protocol` documentation (mdn#39000)
  Improve the clarity of a sentence (mdn#39003)
  Change if...else to non-zero on control_flow page (mdn#38992)
  Update group-data of EditContext API (mdn#38842)
  improve description (mdn#38845)
  fix: auto-cleanup by bot (mdn#38990)
  New pages: HTMLFormElement.rel and .relList (mdn#38900)
  Add live sample embeds for SVG path examples (mdn#38928)
  Remove uncovered topic from the introduction (mdn#38986)
  Remove self-links (mdn#38982)
  Synchronize with BCD v6.0.3 (mdn#38983)
  Fix grammar (mdn#38981)
  Remove external Web Speech API articles (mdn#38980)
  Add missing {{Specifications}} macros (mdn#38975)
  Remove browser-compat for CSS types without BCD (mdn#38970)
  Update async_function_star_ to have a working example (mdn#38972)
  ...
@wbamberg wbamberg marked this pull request as ready for review April 6, 2025 21:50
@wbamberg wbamberg requested a review from a team as a code owner April 6, 2025 21:50
@wbamberg wbamberg requested review from hamishwillee and removed request for a team April 6, 2025 21:50
page-type: guide
---

Cross-site leaks (also called XS-leaks) are a class of attack which exploit the ways in which a target site can leak information to an attacker's site, either through web platform APIs or through {{glossary("side channels")}} such as timing attacks. The information leaked could include, for example:
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think a web platform API is a side channel in these cases (i.e. is it worth differentiating)?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the wording is a little unclear: "a class of attack which exploit the ways in which a target site can leak information to an attacker's site". Maybe start with a typical user story, an example of what information is (presumably unintentionally) leaking and then how an attacker exploits that leak?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think a web platform API is a side channel in these cases (i.e. is it worth differentiating)?

yes, tbh I started being uncertain about what exactly counts as a side channel, so I reworked the definition a bit in 36d919e .

Maybe start with a typical user story, an example of what information is (presumably unintentionally) leaking and then how an attacker exploits that leak?

I'm happy to do more work on the definition if it is unclear but I really do want to start with a concise definition rather than a story.


### Defense summary checklist

As we've seen, cross-site leaks include a range of attacks targeting different parts of the web platform: a single defense doesn't work against any of them. Indeed, some leaks, such as the one that exploits CSP to leak redirects, don't have any defenses yet.
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I created a comment against a commit, but it doesn't seem to be visible here. Might be irrelevant

But while there is no way to stop CSP from revealing the redirect, you aren't forced to have redirects. You might, for example, use the same page with frames and different content for a logged in users.

Copy link
Copy Markdown
Collaborator

@hamishwillee hamishwillee left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO this is a useful entry point that positions the problem appropriate, doesn't swamp the reader, and doesn't duplicate other resource. I feel I have a good idea of what kinds of information might leak, what an attacker might do with that information, broad steps I might take to reduce the risk, and I can work out where to go for more information.

Approving, so you can merge when you think inflow of useful review has ceased.

@wbamberg
Copy link
Copy Markdown
Collaborator Author

@mozfreddyb , would love you to take a look when you get the chance.

@hamishwillee
Copy link
Copy Markdown
Collaborator

It would be great to get this in. @mozfreddyb Gentle ping for review as per ^^^

Copy link
Copy Markdown
Contributor

@mozfreddyb mozfreddyb left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks great (sorry about the long wait)

Comment on lines +54 to +64
```js
const url = "https://example.org/admin";
const script = document.createElement("script");

script.addEventListener("load", (e) => {
console.log(`${url} exists`);
});

script.addEventListener("error", (e) => {
console.log(`${url} does not exist`);
});
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you need to explain why script? Can you give an example of a response the web page may give that is an error/non-error depending on user state?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you need to explain why script?

Isn't this explained at line 42:

The attack relies on the ability of a website to load a resource from another site, for example by setting the src attribute of a {{htmlelement("script")}} element to the URL of the resource:

...or am I misunderstanding you?

Can you give an example of a response the web page may give that is an error/non-error depending on user state?

I think I might need you to explain this a bit more for me. AIUI, in this scenario:

  • if the user is logged in, then the page is returned, and loadis fired
  • if the user isn't logged in, then the server returns an error (e.g. 404) and error is fired

I sort of say that in 52, but is there something else/something more I need to say?

Comment on lines +70 to +71
An attacker may even be able to discover a user's ID, by iteratively trying to load pages to see if pages like `https://example.org/users/my_username` exist.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's important to note here (or above) that this is supposed to be a user-specific page, not "my_username" as a thing that is to be replaced. Maybe something like "example.org/personal-profile-settings" would be less nuanced?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To check my understanding here: in this case we have a website that includes the the username in the URL for a page, so if the username is fishfingers and the user is logged in, then the page https://example.org/users/fishfingers exists. So an attacker can repeatedly try to load pages with test IDs, and if a page exits, then they know not only that the current user is logged in, but what their username is.

So (if this is right) maybe it's worth explaining it a bit more, with some version of that paragraph? Does that address your comment?

Comment on lines +85 to +86
Alternatively, the attacker can embed the target site in an {{htmlelement("iframe")}}, and retrieve the frame's {{domxref("HTMLIFrameElement.contentWindow", "contentWindow")}} property:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Worth mentioning that this can be prevented with XFO, but doesn't stop the xs-leak (because of the popup scenario) -- or better leave that out?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd prefer to keep defenses and attacks separate. I mean otherwise, why mention XFO here and not COOP? I do mention frame protection as a defense against this attack, in https://pr38977.review.mdn.allizom.net/en-US/docs/Web/Security/Attacks/XS-leaks#framing_protection.

wbamberg and others added 2 commits May 6, 2025 07:20
Co-authored-by: Brian Smith <brian@smith.berlin>
Co-authored-by: Brian Smith <brian@smith.berlin>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
@martinakraus
Copy link
Copy Markdown

This results in an HTTP request to the https://example.org/ website. If the request includes cookies that the site uses to identify users, and the page requested is only available to logged-in users, then the success or failure of the request reveals whether or not the user is logged in.

Is that really the only possible conclusion? I'm just wondering—if the SameSite attribute is set to Strict, the cookie wouldn't be sent either, right?
Or does it mean that the URL https://example.org/admin is already considered same-origin in this context?

Overall, the introduction to these types of attacks is very well written. The section on defense mechanisms in particular provides a clear and comprehensible overview of possible mitigations.

@wbamberg
Copy link
Copy Markdown
Collaborator Author

This results in an HTTP request to the https://example.org/ website. If the request includes cookies that the site uses to identify users, and the page requested is only available to logged-in users, then the success or failure of the request reveals whether or not the user is logged in.

Is that really the only possible conclusion? I'm just wondering—if the SameSite attribute is set to Strict, the cookie wouldn't be sent either, right? Or does it mean that the URL https://example.org/admin is already considered same-origin in this context?

Right, AFAIK SameSite is a defense here, which I do note at line 206:

This can protect against some cross-site leaks. For example, the Leaking page existence using error events attack depends on the attacker making cross-site resource requests that include the user's session cookies. Setting SameSite to Lax on the user's session cookie would prevent this attack, because the cookie would not be included in the attacker's request, and no pages that require a login would ever be returned.

So in the first half of the doc, that describes the attacks, there's an implicit "if you don't implement any defenses". I think it's hard to avoid from this, given the overall doc structure of presenting first attacks, then defenses.

Copy link
Copy Markdown

@terjanq terjanq left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey! I left some comments, but overall looks really good! :)

- [Cross-site request forgery (CSRF)](/en-US/docs/Web/Security/Attacks/CSRF)
- : In a cross-site request forgery (CSRF) attack, an attacker tricks the user or the browser into making an HTTP request to the target site from a malicious site. The request includes the user's credentials and causes the server to carry out some harmful action, thinking that the user intended it.
- [Cross-site leaks (XS-leaks)](/en-US/docs/Web/Security/Attacks/XS-leaks)
- : Cross-site leaks (XS-leaks) are a class of attack which exploit the ways in which a target site can leak information to an attacker's site, either through web platform APIs or through {{glossary("side channels")}} such as timing attacks.
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This sentence reads a little like it's a target site that intentionally or internationally leaks information to an attacker's site which isn't how xs-leaks usually work. It's more like an attacker's site is able to infer information about a target site - not the other way around.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

-> 5ce8cb6

page-type: guide
---

Cross-site leaks (also called XS-leaks) are a class of attack in which an attacker's site can derive information about the target site, or about the user's relationship with the target site, by using web platform APIs that enable sites to interact with one another. The information leaked could include, for example:
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We usually refer to cross-site leaks as XS-Leaks with L capitalized

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

-> 43754cb


In this section we'll describe three different cross-site leaks, to give an idea of how they work.

- [Leaking page existence using error events](#leaking_page_existence_using_error_events): in this attack, an attacker can determine whether particular pages in the target site exist, by attempting to load them as resources and listening for the {{domxref("HTMLElement/error_event", "error")}} and {{domxref("HTMLElement/load_event", "load")}} events. If certain pages are only available to logged-in users, the attacker can determine whether the user is signed into the target site.
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that "page existence" used in this paragraph is quite confusing. I am assuming you're talking about endpoints that return HTTP 200 vs those that return HTTP 404 or HTTP 500? I would clarify that here that it's about a status code rather than an existence of an endpoint, or that this measures user's access to the page rather than its existence.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

-> 3566fa1

> [!NOTE]
> For a more complete catalog of cross-site leaks, see the [XS-Leaks Wiki](https://xsleaks.dev/) and the [OWASP Cross-site Leaks Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/XS_Leaks_Cheat_Sheet.html).

### Leaking page existence using error events
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similarly to the other content, this would benefit from explaining where the intercepted errors are coming from - usually because of HTTP200 vs HTTP404/HTTP500

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

-> 3566fa1

Comment on lines +109 to +128
```html
<!DOCTYPE html>
<html>
<head>
<meta
http-equiv="Content-Security-Policy"
content="frame-src https://example.org/admin" />
</head>
<body>
<script>
document.addEventListener("securitypolicyviolation", () => {
console.log("Page was redirected");
});
const frame = document.createElement("iframe");
document.body.appendChild(frame);
frame.src = "https://example.org/admin";
</script>
</body>
</html>
```
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this won't work. Leaking redirects with CSP mostly works if the redirect is cross-origin as if I remember correctly the path after redirection is ignored.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, could be. I don't think I tested this with a same-origin redirect, I tested with GitLab, which redirects gitlab.com to about.gitlab.com for not-logged-in visitors. So I suppose I could change this to admin.example.org?

Still I am surprised it wouldn't work same-origin. Doesn't frame-src https://example.org/admin forbid https://example.org/login, per https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Content-Security-Policy#host-source ?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

-> 944a505

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still I am surprised it wouldn't work same-origin. Doesn't frame-src https://example.org/admin forbid https://example.org/login

It does, but it onlly applies to the original URL not the one after redirects. This is to prevent information leaks if I remember correctly.


## Defenses against cross-site leaks

Cross-site leaks exploit mechanisms in the web platform which enables websites to interact with each other. Correspondingly, the defenses against cross-site leaks all involve _isolating_ the target website from potential attackers, by disabling or controlling these cross-site interactions.
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the defenses against cross-site leaks all involve

Not all protections involve isolating the target. For example, adding XSRF tokens to subresources do not isolate the target but prevent an attacker from probing these resources.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

-> ccf9aa3


```js
function isAllowed(req) {
// Allow same-origin, same-site, and user-initiated requests
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"none" are either user initiated or browser initiated, I think.

Copy link
Copy Markdown
Collaborator Author

@wbamberg wbamberg May 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you elaborate what the difference is? The spec only talks about user-initiated (https://w3c.github.io/webappsec-fetch-metadata/#sec-fetch-site-header) and links to some more detail at https://w3c.github.io/webappsec-fetch-metadata/#directly-user-initiated.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Didn't know that the spec tries to be that explicit about "none" and I'm surprised there is no mention about browser initiated requests but maybe I'm hallucitating. I thought that some of the background requests the browser sends come with Sec-Fetch-Site: none but maybe the headers are just omitted in that scenario. Let's leave it how it is for now :)


If `frame-ancestors` and `X-Frame-Options` are both set, then browsers that support `frame-ancestors` will ignore `X-Frame-Options`. This means that there's no reason not to set `X-Frame-Options` as well as `frame-ancestors`, and thus prevent embedding even in browsers that don't support `frame-ancestors`.

### Cross-Origin Opener Policy (COOP)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would maybe suggest mentioning that COOP in many cases limits the attacks to 1 bit of information per user activation because the reference is "burned" after COOP headers are encountered. Example 1 bit attacks that are possible with COOP are timing based attacks - you can measure the time it takes to lose reference to the window.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if this is a bit too advanced/nuanced for this guide.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From the experience COOP cause a little confusion because it's presented as a complete mitigation against navigation based xsleaks but it's a great to mitigate the risks rather than fully mitigate them. It is a little bit advanced/nuanced so it's your call in the end whether it's worth mentioning the nuance :)

@wbamberg
Copy link
Copy Markdown
Collaborator Author

Thank you for the review @terjanq ! I made most of the changes and commented on a couple. Please let me know if you're happy with this.

@terjanq
Copy link
Copy Markdown

terjanq commented May 13, 2025

Thank you for the review @terjanq ! I made most of the changes and commented on a couple. Please let me know if you're happy with this.

Looks great, thanks!

Co-authored-by: Brian Smith <brian@smith.berlin>
@wbamberg
Copy link
Copy Markdown
Collaborator Author

I'm merging this: it's has a tick from Hamish and an implicit tick from one expert reviewer. There are a few unresolved comments but I'd like to take these as follow-ups if needed - in general people seem pretty happy with it.

@wbamberg wbamberg merged commit 8a9c514 into mdn:main May 13, 2025
8 checks passed
@mozfreddyb
Copy link
Copy Markdown
Contributor

Sorry I did not find the time to go through the article more thoroughly before it was merged. This looks great!
I really appreciate you jumping in @terjanq 😍

wbamberg added a commit to wbamberg/content that referenced this pull request May 21, 2025
* upstream/main: (40 commits)
  Add a guide on xs-leaks (mdn#38977)
  chore: Link to child pages in SVG, URI section (mdn#39470)
  Correct page title for `Uint8Array.prototype.setFromHex()` (mdn#39494)
  Bump markdownlint-cli2 from 0.17.2 to 0.18.0 (mdn#39488)
  Add more documentation for sizes=auto (mdn#39464)
  css(fix): update 'shape()' page (mdn#39454)
  Expand Compression Dictionary format description (mdn#39441)
  Add username/password section to URLs guide (mdn#39487)
  Fix typos (mdn#39481)
  CSS: mask-position property description (mdn#39449)
  Adding @starting-style to CSS nesting at-rules (mdn#39473)
  feat(aria): Add short titles, format titles for roles (mdn#39472)
  Fix Circle Collision detection algorithm (mdn#39484)
  Add warning against using element IDs as global properties. (mdn#39422)
  Fix typos in web audio spatialization basics (mdn#39476)
  More Error Messages (mdn#39419)
  Add links to CSS sidebar (mdn#39445)
  css: add 'stretch' value examples (mdn#39451)
  chore: link to unlinked child pages (mdn#39469)
  Fix typos (mdn#39465)
  ...
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Content:Security Security docs size/m [PR only] 51-500 LoC changed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Suggestion: New article on cross-site leaks (xsleaks)

8 participants