From 7fd5fc4b5c33aed71fc42bcb65a69484a157d56e Mon Sep 17 00:00:00 2001 From: Andrea Marchesini Date: Mon, 19 Nov 2018 19:16:25 +0100 Subject: [PATCH] Bug 1507769 - User-interaction required before granting storage access for some 3rd party trackers, r=ehsan We want to introduce a new pref to block trackers that try to workaround our heuristic. The pref is called: privacy.restrict3rdpartystorage.userInteractionRequiredForHosts --- modules/libpref/init/all.js | 4 ++ .../antitracking/AntiTrackingCommon.cpp | 7 +- .../browser/browser_localStorageEvents.js | 66 ++++++++++++++++++- .../antitracking/test/browser/head.js | 1 + .../test/browser/localStorage.html | 16 +++++ 5 files changed, 90 insertions(+), 4 deletions(-) diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js index 3fa6fb6bd942..a4729f3f9300 100644 --- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -1375,6 +1375,10 @@ pref("privacy.popups.disable_from_plugins", 3); // Enable Paritioned LocalStorage for a list of hosts. pref("privacy.restrict3rdpartystorage.partitionedHosts", "accounts.google.com/o/oauth2/"); +// If a host is contained in this pref list, user-interaction is required +// before granting the storage access permission. +pref("privacy.restrict3rdpartystorage.userInteractionRequiredForHosts", ""); + // Excessive reporting of blocked popups can be a DOS vector, // by overloading the main process as popups get blocked and when // users try to restore all popups, which is the most visible diff --git a/toolkit/components/antitracking/AntiTrackingCommon.cpp b/toolkit/components/antitracking/AntiTrackingCommon.cpp index 82031f456063..f4c4379d3677 100644 --- a/toolkit/components/antitracking/AntiTrackingCommon.cpp +++ b/toolkit/components/antitracking/AntiTrackingCommon.cpp @@ -504,12 +504,15 @@ AntiTrackingCommon::AddFirstPartyStorageAccessGrantedFor(nsIPrincipal* aPrincipa // We hardcode this block reason since the first-party storage access // permission is granted for the purpose of blocking trackers. - // Note that if aReason is eOpenerAfterUserInteraction, we don't check the + // Note that if aReason is eOpenerAfterUserInteraction and the + // trackingPrincipal is not in a blacklist, we don't check the // user-interaction state, because it could be that the current process has // just sent the request to store the user-interaction permission into the // parent, without having received the permission itself yet. const uint32_t blockReason = nsIWebProgressListener::STATE_COOKIES_BLOCKED_TRACKER; - if (aReason != eOpenerAfterUserInteraction && + if ((aReason != eOpenerAfterUserInteraction || + nsContentUtils::IsURIInPrefList(trackingURI, + "privacy.restrict3rdpartystorage.userInteractionRequiredForHosts")) && !HasUserInteraction(trackingPrincipal)) { LOG_SPEC(("Tracking principal (%s) hasn't been interacted with before, " "refusing to add a first-party storage permission to access it", diff --git a/toolkit/components/antitracking/test/browser/browser_localStorageEvents.js b/toolkit/components/antitracking/test/browser/browser_localStorageEvents.js index 7006794bb82e..765309beecb8 100644 --- a/toolkit/components/antitracking/test/browser/browser_localStorageEvents.js +++ b/toolkit/components/antitracking/test/browser/browser_localStorageEvents.js @@ -71,9 +71,71 @@ add_task(async function testLocalStorageEventPropagation() { info("Removing the tab"); BrowserTestUtils.removeTab(tab); -}); -add_task(async function() { + info("Cleaning up."); + await new Promise(resolve => { + Services.clearData.deleteData(Ci.nsIClearDataService.CLEAR_ALL, value => resolve()); + }); +}); + +add_task(async function testBlockedLocalStorageEventPropagation() { + await SpecialPowers.pushPrefEnv({"set": [ + ["privacy.restrict3rdpartystorage.userInteractionRequiredForHosts", "tracking.example.com,tracking.example.org"], + ]}); + + info("Creating a new tab"); + let tab = BrowserTestUtils.addTab(gBrowser, TEST_TOP_PAGE); + gBrowser.selectedTab = tab; + + let browser = gBrowser.getBrowserForTab(tab); + await BrowserTestUtils.browserLoaded(browser); + + info("Loading tracking scripts"); + await ContentTask.spawn(browser, { + page: TEST_3RD_PARTY_DOMAIN + TEST_PATH + "localStorage.html", + }, async obj => { + info("Creating tracker iframe"); + + let ifr = content.document.createElement("iframe"); + ifr.src = obj.page; + + await new content.Promise(resolve => { + ifr.onload = function() { + resolve(); + }; + content.document.body.appendChild(ifr); + }); + + info("LocalStorage should be blocked."); + await new content.Promise(resolve => { + content.addEventListener("message", e => { + if (e.data.type == "test") { + is(e.data.status, false, "LocalStorage blocked"); + } else { + ok(false, "Unknown message"); + } + resolve(); + }, {once: true}); + ifr.contentWindow.postMessage("test", "*"); + }); + + info("Let's open the popup"); + await new content.Promise(resolve => { + content.addEventListener("message", e => { + if (e.data.type == "test") { + is(e.data.status, false, "LocalStorage still blocked"); + } else { + ok(false, "Unknown message"); + } + resolve(); + }, {once: true}); + ifr.contentWindow.postMessage("open and test", "*"); + }); + }); + + info("Removing the tab"); + BrowserTestUtils.removeTab(tab); + info("Cleaning up."); await new Promise(resolve => { Services.clearData.deleteData(Ci.nsIClearDataService.CLEAR_ALL, value => resolve()); diff --git a/toolkit/components/antitracking/test/browser/head.js b/toolkit/components/antitracking/test/browser/head.js index c24398073003..b0f73d8b3594 100644 --- a/toolkit/components/antitracking/test/browser/head.js +++ b/toolkit/components/antitracking/test/browser/head.js @@ -255,6 +255,7 @@ this.AntiTracking = { ["privacy.trackingprotection.annotate_channels", cookieBehavior != BEHAVIOR_ACCEPT], [win.ContentBlocking.prefIntroCount, win.ContentBlocking.MAX_INTROS], ["browser.fastblock.enabled", false], // prevent intermittent failures + ["privacy.restrict3rdpartystorage.userInteractionRequiredForHosts", "tracking.example.com,tracking.example.org"], ]}); if (extraPrefs && Array.isArray(extraPrefs) && extraPrefs.length) { diff --git a/toolkit/components/antitracking/test/browser/localStorage.html b/toolkit/components/antitracking/test/browser/localStorage.html index 41d0c5240c86..934d7a5efa67 100644 --- a/toolkit/components/antitracking/test/browser/localStorage.html +++ b/toolkit/components/antitracking/test/browser/localStorage.html @@ -24,6 +24,22 @@ if (parent) { if (e.data == "open") { window.open("localStorage.html"); + return; + } + + if (e.data == "open and test") { + let w = window.open("localStorage.html"); + w.addEventListener("load", _ => { + let status; + try { + localStorage.foo = "value" + Math.random(); + status = true; + } catch (e) { + status = false; + } + + parent.postMessage({type: "test", status }, "*"); + }, {once: true}); } };