Bug 1661151 - The Storage Access API should automatically reject access for cookie policies that don't allow cross-site storage access r=anti-tracking-reviewers,timhuang

Add a test to validate behavior on non-tracking first-party requests
    Add tests to ensure we don't say we are granting storage access to third parties with cookiePolicies that forbid it
    Add check near the top of Document::HasStorageAccess to immediately return false when cookiePolicy is REJECT.
    Add check near the top of Document::RequestStorageAccess to reject when cookiePolicy is REJECT.
    Add check in Document::RequestStorageAccess to reject when the cookie policy forbids third party cookies
        Note, BEHAVIOR_LIMIT_FOREIGN is treated like BEHAVIOR_REJECT_FOREIGN, just like in ContentBlocking::ShouldAllowAccessFor
    Add comparable checks to the priveleged version of RequestStorageAccess

This also resolves Bug 1661152

Differential Revision: https://phabricator.services.mozilla.com/D129278
This commit is contained in:
Benjamin VanderSloot 2021-10-25 23:20:39 +00:00
parent e9b6e43371
commit f9d8931809
5 changed files with 161 additions and 1 deletions

View File

@ -16546,6 +16546,11 @@ already_AddRefed<mozilla::dom::Promise> Document::HasStorageAccess(
return promise.forget();
}
if (CookieJarSettings()->GetBlockingAllContexts()) {
promise->MaybeResolve(false);
return promise.forget();
}
if (IsTopLevelContentDocument()) {
promise->MaybeResolve(true);
return promise.forget();
@ -16642,6 +16647,13 @@ already_AddRefed<mozilla::dom::Promise> Document::RequestStorageAccess(
return promise.forget();
}
// Step 0. If the browser forbids any storage access, reject.
if (CookieJarSettings()->GetBlockingAllContexts()) {
this->ConsumeTransientUserGestureActivation();
promise->MaybeRejectWithUndefined();
return promise.forget();
}
// Step 1. If the document already has been granted access, resolve.
RefPtr<nsGlobalWindowOuter> outer =
nsGlobalWindowOuter::Cast(inner->GetOuterWindow());
@ -16738,6 +16750,13 @@ already_AddRefed<mozilla::dom::Promise> Document::RequestStorageAccess(
// Examples: skip-lists, on-device classification,
// user settings, anti-clickjacking heuristics, or prompting the
// user for explicit permission. Reject if some rule is not fulfilled.
if (CookieJarSettings()->GetBlockingAllThirdPartyContexts()) {
this->ConsumeTransientUserGestureActivation();
promise->MaybeRejectWithUndefined();
return promise.forget();
}
if (CookieJarSettings()->GetRejectThirdPartyContexts()) {
// Only do something special for third-party tracking content.
uint32_t antiTrackingRejectedReason = 0;
@ -16931,6 +16950,13 @@ already_AddRefed<mozilla::dom::Promise> Document::RequestStorageAccessForOrigin(
return nullptr;
}
// If the browser forbids any storage access, reject.
if (CookieJarSettings()->GetBlockingAllContexts()) {
this->ConsumeTransientUserGestureActivation();
promise->MaybeRejectWithUndefined();
return promise.forget();
}
// Only enforce third-party checks when there is a reason to enforce them.
if (!CookieJarSettings()->GetRejectThirdPartyContexts()) {
// If the the thrid party origin is equal to the window's, resolve.
@ -16945,6 +16971,13 @@ already_AddRefed<mozilla::dom::Promise> Document::RequestStorageAccessForOrigin(
}
// Check any additional rules that the browser has.
if (CookieJarSettings()->GetBlockingAllThirdPartyContexts()) {
this->ConsumeTransientUserGestureActivation();
promise->MaybeRejectWithUndefined();
return promise.forget();
}
if (CookieJarSettings()->GetRejectThirdPartyContexts()) {
RefPtr<BrowsingContext> bc = GetBrowsingContext();
if (!bc) {

View File

@ -218,6 +218,26 @@ CookieJarSettings::GetLimitForeignContexts(bool* aLimitForeignContexts) {
return NS_OK;
}
NS_IMETHODIMP
CookieJarSettings::GetBlockingAllThirdPartyContexts(
bool* aBlockingAllThirdPartyContexts) {
// XXX For non-cookie forms of storage, we handle BEHAVIOR_LIMIT_FOREIGN by
// simply rejecting the request to use the storage. In the future, if we
// change the meaning of BEHAVIOR_LIMIT_FOREIGN to be one which makes sense
// for non-cookie storage types, this may change.
*aBlockingAllThirdPartyContexts =
mCookieBehavior == nsICookieService::BEHAVIOR_LIMIT_FOREIGN ||
(!StaticPrefs::network_cookie_rejectForeignWithExceptions_enabled() &&
mCookieBehavior == nsICookieService::BEHAVIOR_REJECT_FOREIGN);
return NS_OK;
}
NS_IMETHODIMP
CookieJarSettings::GetBlockingAllContexts(bool* aBlockingAllContexts) {
*aBlockingAllContexts = mCookieBehavior == nsICookieService::BEHAVIOR_REJECT;
return NS_OK;
}
NS_IMETHODIMP
CookieJarSettings::GetPartitionForeign(bool* aPartitionForeign) {
*aPartitionForeign =

View File

@ -36,6 +36,9 @@ interface nsICookieJarSettings : nsISerializable
[infallible] readonly attribute boolean limitForeignContexts;
[infallible] readonly attribute boolean blockingAllThirdPartyContexts;
[infallible] readonly attribute boolean blockingAllContexts;
/**
* Whether our cookie behavior mandates partitioning third-party content.
*/

View File

@ -4,6 +4,12 @@
/* import-globals-from antitracking_head.js */
var settings = [
// same-origin no-tracker
{
name: "Test whether same-origin non-tracker frame has storage access",
topPage: TEST_TOP_PAGE,
thirdPartyPage: TEST_DOMAIN + TEST_PATH + "3rdParty.html",
},
// 3rd-party no-tracker
{
name: "Test whether 3rd-party non-tracker frame has storage access",
@ -68,6 +74,7 @@ var testCases = [
{
behavior: BEHAVIOR_ACCEPT, // 0
hasStorageAccess: [
true /* same-origin non-tracker */,
true /* 3rd-party non-tracker */,
true /* 3rd-party non-tracker with permission */,
true /* 3rd-party tracker */,
@ -79,6 +86,7 @@ var testCases = [
{
behavior: BEHAVIOR_REJECT_FOREIGN, // 1
hasStorageAccess: [
true /* same-origin non-tracker */,
false /* 3rd-party non-tracker */,
SpecialPowers.Services.prefs.getBoolPref(
"network.cookie.rejectForeignWithExceptions.enabled"
@ -94,17 +102,19 @@ var testCases = [
{
behavior: BEHAVIOR_REJECT, // 2
hasStorageAccess: [
false /* same-origin non-tracker */,
false /* 3rd-party non-tracker */,
false /* 3rd-party non-tracker with permission */,
false /* 3rd-party tracker */,
false /* 3rd-party tracker with permission */,
false /* same-site tracker */,
true /* same-origin tracker */,
false /* same-origin tracker */,
],
},
{
behavior: BEHAVIOR_LIMIT_FOREIGN, // 3
hasStorageAccess: [
true /* same-origin non-tracker */,
false /* 3rd-party non-tracker */,
false /* 3rd-party non-tracker with permission */,
false /* 3rd-party tracker */,
@ -116,6 +126,7 @@ var testCases = [
{
behavior: BEHAVIOR_REJECT_TRACKER, // 4
hasStorageAccess: [
true /* same-origin non-tracker */,
true /* 3rd-party non-tracker */,
true /* 3rd-party non-tracker with permission */,
false /* 3rd-party tracker */,
@ -127,6 +138,7 @@ var testCases = [
{
behavior: BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN, // 5
hasStorageAccess: [
true /* same-origin non-tracker */,
false /* 3rd-party non-tracker */,
true /* 3rd-party non-tracker with permission */,
false /* 3rd-party tracker */,

View File

@ -61,3 +61,95 @@ add_task(async _ => {
);
});
});
AntiTracking._createTask({
name:
"Test that we never grant access to cookieBehavior=1, when network.cookie.rejectForeignWithExceptions.enabled is set to false",
cookieBehavior: BEHAVIOR_REJECT_FOREIGN,
allowList: false,
callback: async _ => {
/* import-globals-from storageAccessAPIHelpers.js */
await noStorageAccessInitially();
await callRequestStorageAccess(null, true);
},
extraPrefs: [["network.cookie.rejectForeignWithExceptions.enabled", false]],
expectedBlockingNotifications: 0,
runInPrivateWindow: false,
iframeSandbox: null,
accessRemoval: null,
callbackAfterRemoval: null,
thirdPartyPage: TEST_3RD_PARTY_PAGE_HTTP,
errorMessageDomains: [
"http://tracking.example.org",
"http://tracking.example.org",
],
});
add_task(async _ => {
await new Promise(resolve => {
Services.clearData.deleteData(Ci.nsIClearDataService.CLEAR_ALL, value =>
resolve()
);
});
});
AntiTracking._createTask({
name: "Test that we never grant access to cookieBehavior=2",
cookieBehavior: BEHAVIOR_REJECT,
allowList: false,
callback: async _ => {
/* import-globals-from storageAccessAPIHelpers.js */
await noStorageAccessInitially();
await callRequestStorageAccess(null, true);
},
expectedBlockingNotifications: 0,
runInPrivateWindow: false,
iframeSandbox: null,
accessRemoval: null,
callbackAfterRemoval: null,
thirdPartyPage: TEST_3RD_PARTY_PAGE_HTTP,
errorMessageDomains: [
"http://tracking.example.org",
"http://tracking.example.org",
],
});
add_task(async _ => {
await new Promise(resolve => {
Services.clearData.deleteData(Ci.nsIClearDataService.CLEAR_ALL, value =>
resolve()
);
});
});
AntiTracking._createTask({
name: "Test that we never grant access to cookieBehavior=3",
cookieBehavior: BEHAVIOR_LIMIT_FOREIGN,
allowList: false,
callback: async _ => {
/* import-globals-from storageAccessAPIHelpers.js */
await noStorageAccessInitially();
await callRequestStorageAccess(null, true);
},
expectedBlockingNotifications: 0,
runInPrivateWindow: false,
iframeSandbox: null,
accessRemoval: null,
callbackAfterRemoval: null,
thirdPartyPage: TEST_3RD_PARTY_PAGE_HTTP,
errorMessageDomains: [
"http://tracking.example.org",
"http://tracking.example.org",
],
});
add_task(async _ => {
await new Promise(resolve => {
Services.clearData.deleteData(Ci.nsIClearDataService.CLEAR_ALL, value =>
resolve()
);
});
});