Bug 1507769 - User-interaction required before granting storage access for some 3rd party trackers, r=ehsan
☠☠ backed out by 29ff944efa40 ☠ ☠
authorAndrea Marchesini <amarchesini@mozilla.com>
Mon, 19 Nov 2018 19:16:25 +0100
changeset 503528 401763b97e54cd99c6a5ad2a617379da011a57e5
parent 503527 838dfe679fddff427f3e8b8ded6e356437c6110e
child 503529 9c14b7e88ab7a046c281815707916d76cbca3cb2
push id10290
push userffxbld-merge
push dateMon, 03 Dec 2018 16:23:23 +0000
treeherdermozilla-beta@700bed2445e6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersehsan
bugs1507769
milestone65.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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
toolkit/components/antitracking/AntiTrackingCommon.cpp
toolkit/components/antitracking/test/browser/browser_localStorageEvents.js
toolkit/components/antitracking/test/browser/head.js
toolkit/components/antitracking/test/browser/localStorage.html
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -1370,16 +1370,20 @@ pref("content.sink.pending_event_mode", 
 //   1 = openControlled
 //   2 = openBlocked
 //   3 = openAbused
 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
 // option in our UI at the time of writing.
 // We will invisibly drop any popups from a page that has already
 // opened more than this number of popups.
 pref("privacy.popups.maxReported", 100);
 
--- a/toolkit/components/antitracking/AntiTrackingCommon.cpp
+++ b/toolkit/components/antitracking/AntiTrackingCommon.cpp
@@ -499,22 +499,25 @@ AntiTrackingCommon::AddFirstPartyStorage
   nsPIDOMWindowInner* topInnerWindow = topWindow->GetCurrentInnerWindow();
   if (NS_WARN_IF(!topInnerWindow)) {
     LOG(("No top inner window."));
     return StorageAccessGrantPromise::CreateAndReject(false, __func__);
   }
 
   // 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",
               _spec), trackingURI);
     NotifyRejection(aParentWindow, blockReason);
     return StorageAccessGrantPromise::CreateAndReject(false, __func__);
   }
 
--- a/toolkit/components/antitracking/test/browser/browser_localStorageEvents.js
+++ b/toolkit/components/antitracking/test/browser/browser_localStorageEvents.js
@@ -66,16 +66,78 @@ add_task(async function testLocalStorage
         resolve();
       }, {once: true});
       ifr.contentWindow.postMessage("open", "*");
     });
   });
 
   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());
+  });
+});
--- a/toolkit/components/antitracking/test/browser/head.js
+++ b/toolkit/components/antitracking/test/browser/head.js
@@ -250,16 +250,17 @@ this.AntiTracking = {
       ["browser.contentblocking.allowlist.annotations.enabled", blockingByContentBlockingRTUI],
       ["browser.contentblocking.allowlist.storage.enabled", blockingByContentBlockingRTUI],
       ["network.cookie.cookieBehavior", cookieBehavior],
       ["privacy.trackingprotection.enabled", false],
       ["privacy.trackingprotection.pbmode.enabled", false],
       ["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) {
       await SpecialPowers.pushPrefEnv({"set": extraPrefs });
     }
 
     await UrlClassifierTestUtils.addTestTrackers();
   },
--- a/toolkit/components/antitracking/test/browser/localStorage.html
+++ b/toolkit/components/antitracking/test/browser/localStorage.html
@@ -19,16 +19,32 @@ if (parent) {
       }
 
       parent.postMessage({type: "test", status }, "*");
       return;
     }
 
     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});
     }
   };
 
   window.addEventListener("storage", e => {
     let fromOpener = localStorage.foo.startsWith("opener");
 
     let status;
     try {