Bug 1876504 - Also allow a same-origin initiated iframe to get storage access from its parent - r=anti-tracking-reviewers,timhuang

Differential Revision: https://phabricator.services.mozilla.com/D203313
This commit is contained in:
Benjamin VanderSloot 2024-04-15 15:28:11 +00:00
Родитель 271aab4b32
Коммит d0f8a1c146
6 изменённых файлов: 149 добавлений и 6 удалений

Просмотреть файл

@ -287,6 +287,13 @@ async function MaybeSetStorageAccess(origin, embedding_origin, value) {
}
}
// Navigate the inner iframe using the given frame.
function NavigateChild(frame, url) {
return PostMessageAndAwaitReply(
{ command: "navigate_child", url }, frame.contentWindow);
}
// Starts a dedicated worker in the given frame.
function StartDedicatedWorker(frame) {
return PostMessageAndAwaitReply(

Просмотреть файл

@ -0,0 +1,65 @@
// META: script=helpers.js
// META: script=/cookies/resources/cookie-helper.sub.js
// META: script=/resources/testdriver.js
// META: script=/resources/testdriver-vendor.js
'use strict';
(async function() {
// This is cross-domain from the current document.
const altWww = "https://{{hosts[alt][www]}}:{{ports[https][0]}}";
const altRoot = "https://{{hosts[alt][]}}:{{ports[https][0]}}";
const responderPath = "/storage-access-api/resources/script-with-cookie-header.py?script=embedded_responder.js";
const forwarderPath = "/storage-access-api/resources/script-with-cookie-header.py?script=embedded_forwarder.js";
const altWwwResponder = `${altWww}${responderPath}`;
const altRootResponder = `${altRoot}${responderPath}`;
const altWwwNestedCrossOriginResponder = `${altRoot}${forwarderPath}&inner_url=${encodeURI(altWwwResponder)}`;
async function SetUpResponderFrame(t, url) {
const frame = await CreateFrame(url);
await SetPermissionInFrame(frame, [{ name: 'storage-access' }, 'granted']);
t.add_cleanup(async () => {
await test_driver.delete_all_cookies();
await SetPermissionInFrame(frame, [{ name: 'storage-access' }, 'prompt']);
await MaybeSetStorageAccess("*", "*", "allowed");
});
assert_false(await FrameHasStorageAccess(frame), "frame initially does not have storage access.");
assert_false(await HasUnpartitionedCookie(frame), "frame initially does not have access to cookies.");
assert_true(await RequestStorageAccessInFrame(frame), "requestStorageAccess resolves without requiring a gesture.");
assert_true(await FrameHasStorageAccess(frame), "frame has storage access after request.");
assert_true(await HasUnpartitionedCookie(frame), "frame has access to cookies after request.");
return frame;
}
promise_test(async (t) => {
await MaybeSetStorageAccess("*", "*", "blocked");
await SetFirstPartyCookieAndUnsetStorageAccessPermission(altWww);
const frame = await SetUpResponderFrame(t, altWwwNestedCrossOriginResponder);
await NavigateChild(frame, altWwwResponder);
assert_true(await FrameHasStorageAccess(frame), "innermost frame has storage access after refresh.");
assert_true(await HasUnpartitionedCookie(frame), "innermost frame has access to cookies after refresh.");
}, "Same-site-initiated same-origin navigations preserve storage access");
promise_test(async (t) => {
await MaybeSetStorageAccess("*", "*", "blocked");
await SetFirstPartyCookieAndUnsetStorageAccessPermission(altWww);
const frame = await SetUpResponderFrame(t, altWwwNestedCrossOriginResponder);
await NavigateChild(frame, altRootResponder);
assert_false(await FrameHasStorageAccess(frame), "innermost frame has no storage access after refresh.");
assert_false(await HasUnpartitionedCookie(frame), "innermost frame has no access to cookies after refresh.");
let cookieOnLoad = await GetHTTPCookiesFromFrame(frame);
assert_false(cookieStringHasCookie("cookie", "unpartitioned", cookieOnLoad), "innermost frame has cookie in initial load");
}, "Same-site-initiated cross-origin navigations do not preserve storage access");
})();

Просмотреть файл

@ -0,0 +1,50 @@
"use strict";
test_driver.set_test_context(window.top);
function waitForMessage(timestamp) {
return new Promise(resolve => {
const listener = (event) => {
if (!timestamp || event.data.timestamp == timestamp) {
window.removeEventListener("message", listener);
resolve(event.data);
}
};
window.addEventListener("message", listener);
});
}
var iframe = document.createElement('iframe');
const queryString = window.location.search;
const urlParams = new URLSearchParams(queryString);
iframe.src = urlParams.get("inner_url");
document.body.appendChild(iframe);
window.addEventListener("message", async (event) => {
function replyToParent(data) {
parent.postMessage(
{timestamp: event.data.timestamp, data}, "*");
}
if (!event.data["command"]) {
return;
}
switch (event.data["command"]) {
case "navigate_child":
iframe.onload = () => replyToParent(event.data.url);
iframe.src = event.data.url;
break;
case "reload":
case "navigate":
iframe.contentWindow.postMessage({timestamp, ...event.data}, "*");
break;
default:{
const timestamp = event.data.timestamp;
const p = waitForMessage(timestamp);
iframe.contentWindow.postMessage({timestamp, ...event.data}, "*");
replyToParent(await p.then(resp => resp.data));
break;
}
}
});

Просмотреть файл

@ -31,7 +31,7 @@ function connectAndGetRequestCookiesFrom(origin) {
window.addEventListener("message", async (event) => {
function reply(data) {
event.source.postMessage(
{timestamp: event.data.timestamp, data}, event.origin);
{timestamp: event.data.timestamp, data}, "*");
}
switch (event.data["command"]) {

Просмотреть файл

@ -13,7 +13,10 @@ def main(request, response):
var httpCookies = "%s";
</script>
<body>
<script src="%s"></script>
</body>
""" % (cookie_header, script)
return (200, [], body)

Просмотреть файл

@ -567,9 +567,8 @@ AntiTrackingUtils::GetStoragePermissionStateInParent(nsIChannel* aChannel) {
if (policyType == ExtContentPolicy::TYPE_SUBDOCUMENT) {
// For loads of framed documents, we only use storage access
// if the load is the result of a same-origin, self-initiated
// if the load is the result of a same-origin, same-site-initiated
// navigation of the frame.
uint64_t targetWindowIdNoTop = bc->GetCurrentInnerWindowId();
uint64_t triggeringWindowId;
rv = loadInfo->GetTriggeringWindowId(&triggeringWindowId);
if (NS_WARN_IF(NS_FAILED(rv))) {
@ -581,10 +580,29 @@ AntiTrackingUtils::GetStoragePermissionStateInParent(nsIChannel* aChannel) {
if (NS_WARN_IF(NS_FAILED(rv))) {
return nsILoadInfo::NoStoragePermission;
}
RefPtr<net::HttpBaseChannel> httpChannel = do_QueryObject(aChannel);
if (targetWindowIdNoTop == triggeringWindowId &&
triggeringWindowHasStorageAccess &&
nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
RefPtr<nsIPrincipal> channelResultPrincipal;
rv = ssm->GetChannelResultPrincipal(aChannel,
getter_AddRefs(channelResultPrincipal));
if (NS_WARN_IF(NS_FAILED(rv))) {
return nsILoadInfo::NoStoragePermission;
}
RefPtr<net::HttpBaseChannel> httpChannel = do_QueryObject(aChannel);
bool crossSiteInitiated = false;
if (bc && bc->GetParent()->GetCurrentWindowContext()) {
RefPtr<WindowGlobalParent> triggeringWGP =
WindowGlobalParent::GetByInnerWindowId(triggeringWindowId);
if (triggeringWGP && triggeringWGP->DocumentPrincipal()) {
rv = triggeringWGP->DocumentPrincipal()->IsThirdPartyPrincipal(
channelResultPrincipal, &crossSiteInitiated);
if (NS_FAILED(rv)) {
crossSiteInitiated = false;
}
}
}
if (!crossSiteInitiated && triggeringWindowHasStorageAccess &&
trackingPrincipal->Equals(framePrincipal) && httpChannel &&
!httpChannel->HasRedirectTaintedOrigin()) {
return nsILoadInfo::HasStoragePermission;