Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RFC: Firefox tracking protection is explicitly deteriorated in cross-site request performed by userscript #1467

Open
leonidborisenko opened this issue Mar 9, 2022 · 6 comments

Comments

@leonidborisenko
Copy link

Effect of privacy.firstparty.isolate/privacy.firstparty.isolate.use_site settings (accessible from about:config) is explicitly disabled in cross-site requests made by userscript (GM_xmlhttpRequest/GM.xmlHttpRequest) when ViolentMonkey calls browser.cookies.getAll with argument {url: <url>, firstPartyDomain: null}.

With such call under the hood, userscripts inadvertently leak all (related to <url>) third-party cookies in cross-site requests, without respect to user-enabled isolation bounds computed by Firefox from hostname (scheme, hostname and port when <...>.use_site is set) of URL in location bar of tab where userscript is executed.

Therefore, cross-site requests from userscripts allow tracking by third-party and expected tracking protection is nullified.

Furthermore, Firefox 94 (2021-11-02) added paritionKey to argument of browser.cookies.getAll. paritionKey is related to "Total cookie protection" (aka "state paritioning", "dynamic first-party isolation", "dFPI"). Total cookie protection is enabled when network.cookie.cookieBehavior (or network.cookie.cookieBehavior.pbmode which is used in Private browsing mode) is set to 5. It's set to 5 when "Enhanced tracking protection" in about:preferences#privacy is switched to "Strict".

When paritionKey is omitted, cookies are retrieved only from non-paritioned storage. It can allow tracking after switching from non-"Strict" to "Strict" Enhanced tracking protection. But it also completely misses stored third-party cookies when Total cookie protection is enabled.

See also discussion in erosman/support#431 (FireMonkey support repository).

Relevant information:

@tophf
Copy link
Member

tophf commented Mar 9, 2022

Can you describe the exact steps we should perform?
I want to avoid spending hours digging through the links and the documentation.

P.S. firstPartyDomain was added due to #746.

@leonidborisenko
Copy link
Author

I can totally understand requirement of exact steps to recreate issue. There is only so much time and attention, and there are other issues that are described in more concrete terms.

I can also understand that this is not a common issue or, at least, it's an issue that practically nobody cares about.

I want to help.

However, I'm not using ViolentMonkey and also don't use any userscripts with cross-site requests. So I didn't observe reported behavior in everyday usage.

It's not theoretical though. But creation of minimal publically accessible testbed for userscript requires controlling and populating of three public websites with three different firstPartyDomain's (as computed by Firefox): two first-parties and one common third-party. And, given rules for computing firstPartyDomain, reproducing minimal testbed on localhost is not so simple or obvious (for me).

I did observe documented effect of cookie isolation after enabling first-party isolation and with this knowledge I just wanted to report that bluntly using null as firstPartyDomain in getting cookies for (possibly) cross-site requests has consequences of breaking tracking protection guarantees. These consequences are deducible from differences of calling browser.cookies.getAll with {firstPartyDomain: null} and {firstPartyDomain: <domain, computed by Firefox rules>} (after enabling and running of first-party isolation mechanism).

P.S. firstPartyDomain was added due to #746.

Yeah, I knew that. Using null for firstPartyDomain is an easy fix to make exception go away.

But did you note that Firefox doesn't use null automatically? Why it doesn't, if it's such an obvious fix?

It can't, because it expects information for conforming with enabled first-party isolation, and null is explicit approval for breaking first-party isolation (approval made on behalf of user, but silently, without even asking user whether it agrees with such approval).

@tophf
Copy link
Member

tophf commented Mar 9, 2022

Sorry, I still have no idea what we should be doing here as I'm neither affected nor ever investigated this cookie thing. Personally, I've just been nuking all cookies automatically for the past 10+ years. You seem knowledgeable, which is why I'm asking for your help. Could we simply extract the url's domain and use it in firstPartyDomain?

@leonidborisenko
Copy link
Author

I'm neither affected nor ever investigated this cookie thing

I'm not using Violentmonkey, so I'm not affected by this issue either.

In using FireMonkey (another userscript manager), I've been hit by the same exception as reported in #746. I've reported an issue to FireMonkey and in investigating of solution a question had been asked: "Is there any other userscript manager or other addon that is implementing a similar feature?" (a feature of computing firstPartyDomain value).

So I've checked Violentmonkey code and after discovering that Violentmonkey just passes null as firstPartyDomain, I've reported here consequences of this decision with request for comments (RFC).

Could we simply extract the url's domain and use it in firstPartyDomain?

No, it's not so simple.

There are no clearly documented guidelines and direct answers about how exactly extension should get values of firstPartyDomain and partitionKey if it wants to keep first-party isolation guarantees.

According to point 3. at https://bugzilla.mozilla.org/show_bug.cgi?id=1669716#c10:

Another issue is that it's not obvious to extensions that they have to compute the eTLD+1 of a tab's URL to use as the firstPartyDomain.

eTLD means "effective top-level domain" (a legacy term, now it's called "public suffix") and eTLD+1 means "part of hostname up to (and including) first subdomain of eTLD".

But this instruction only partially covers the algorithm to compute firstPartyDomain.

I've scraped bits of information here and there (results are dumped in linked FireMonkey issue and in first post of this issue). Still, I don't know whether I've found and understood enough data to reason about this question and answer it without mistakes. But at this moment, my research is summarized in following thoughts.

You need to ask user:

  • is first-party isolation active?
  • if it's active, then is "Total cookie protection" active or privacy.firstparty.isolate set to true?
  • if privacy.firstparty.isolate is set to true, then is privacy.firstparty.isolate.use_site set to true also?

There is no API to get all these bits of information within WebExtension without explicitly asking user.

When no first-party isolation is active, then don't pass nor firstPartyDomain, neither partitionKey to browser.cookies.getAll.

If privacy.firstparty.isolate is true, it has a priority over Total cookie protection.

When privacy.firstparty.isolate is true, compute firstPartyDomain by algorithm defined in Firefox source [1] and pass it to browser.cookies.getAll, but don't pass partitionKey.

[1] PopulateTopLevelInfoFromURI

In that algorithm:

  • when privacy.firstparty.isolate is true and privacy.firstparty.isolate.use_site is false, then useSite parameter is false;
  • when privacy.firstparty.isolate is true and privacy.firstparty.isolate.use_site is true, then useSite parameter is true.

When Total cookie protection is active, don't pass firstPartyDomain, but pass paritionKey: {topLevelSite: <url>} in details of browser.cookies.getAll. <url> is the URL in location bar of tab where userscript is executed.


I made an attempt to implement relevant parts of PopulateTopLevelInfoFromURI in JavaScript. See a gist. I don't have prior expirience with Firefox C++ codebase, so my understanding of algorithm might be wrong, therefore JavaScript code may have bugs.

@tophf
Copy link
Member

tophf commented Mar 10, 2022

Since we can't ask this question each time a request is made, we can add a setting in the options, so the users can control the behavior, I guess. Another possibility would be to add firstPartyDomain to GM_xmlhttpRequest so that userscripts can set it as they see fit.

@ghost
Copy link

ghost commented May 31, 2022

Firefox 100+ / Win7, both x64

As far as I know privacy.firstparty.isolate.use_site is still experimental.
I have a note regarding this pref that states "Existing data and site permissions are incompatible and some site exceptions may not work e.g. HTTPS-only mode"
I use First Party Isolate myself:
pref("privacy.firstparty.isolate", true); // DEFAULT=false -- rather than Total Cookie Protection,
Together with:
pref("browser.contentblocking.category", "custom"); and pref("network.cookie.cookieBehavior", 1); // Block 3rd-party cookies
but for above reasons I keep:
pref("privacy.firstparty.isolate.use_site", false); // DEFAULT=false

Also, I've read that FPI enabled together with dFPI enabled may cause problems, hence I disable the latter given the former is enabled:
pref("privacy.dynamic_firstparty.use_site", false); // DEFAULT=true

My 2 cents only.

I'd like to be able to reproduce what is described in this post but I would need links.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants