Conversation
* 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) ...
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
|
Preview URLs Flaws (4)Note! 1 document with no flaws that don't need to be listed. 🎉 URL:
External URLs (4)URL:
(comment last updated: 2025-05-13 21:34:20) |
* 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) ...
…e-leaks * origin/cross-site-leaks:
| 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: |
There was a problem hiding this comment.
I think a web platform API is a side channel in these cases (i.e. is it worth differentiating)?
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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. |
There was a problem hiding this comment.
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.
hamishwillee
left a comment
There was a problem hiding this comment.
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.
|
@mozfreddyb , would love you to take a look when you get the chance. |
|
It would be great to get this in. @mozfreddyb Gentle ping for review as per ^^^ |
mozfreddyb
left a comment
There was a problem hiding this comment.
This looks great (sorry about the long wait)
| ```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`); | ||
| }); |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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
srcattribute 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
erroris fired
I sort of say that in 52, but is there something else/something more I need to say?
| 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. | ||
|
|
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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?
| Alternatively, the attacker can embed the target site in an {{htmlelement("iframe")}}, and retrieve the frame's {{domxref("HTMLIFrameElement.contentWindow", "contentWindow")}} property: | ||
|
|
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
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>
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? 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. |
Right, AFAIK SameSite is a defense here, which I do note at line 206:
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. |
terjanq
left a comment
There was a problem hiding this comment.
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. |
There was a problem hiding this comment.
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.
| 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: |
There was a problem hiding this comment.
We usually refer to cross-site leaks as XS-Leaks with L capitalized
|
|
||
| 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. |
There was a problem hiding this comment.
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.
| > [!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 |
There was a problem hiding this comment.
Similarly to the other content, this would benefit from explaining where the intercepted errors are coming from - usually because of HTTP200 vs HTTP404/HTTP500
| ```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> | ||
| ``` |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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 ?
There was a problem hiding this comment.
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. |
There was a problem hiding this comment.
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.
|
|
||
| ```js | ||
| function isAllowed(req) { | ||
| // Allow same-origin, same-site, and user-initiated requests |
There was a problem hiding this comment.
"none" are either user initiated or browser initiated, I think.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
I wonder if this is a bit too advanced/nuanced for this guide.
There was a problem hiding this comment.
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 :)
|
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>
|
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. |
|
Sorry I did not find the time to go through the article more thoroughly before it was merged. This looks great! |
* 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) ...
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.