diff --git a/caps/OriginAttributes.cpp b/caps/OriginAttributes.cpp index ca2c47054b78..e2a219389122 100644 --- a/caps/OriginAttributes.cpp +++ b/caps/OriginAttributes.cpp @@ -28,6 +28,9 @@ static void MakeTopLevelInfo(const nsACString& aScheme, const nsACString& aHost, return; } + // Note: If you change the serialization of the partition-key, please update + // StoragePrincipalHelper.cpp too. + nsAutoCString site; site.AssignLiteral("("); site.Append(aScheme); diff --git a/netwerk/protocol/http/nsHttpChannel.cpp b/netwerk/protocol/http/nsHttpChannel.cpp index 48f3338b37f1..9484de50e70e 100644 --- a/netwerk/protocol/http/nsHttpChannel.cpp +++ b/netwerk/protocol/http/nsHttpChannel.cpp @@ -587,17 +587,18 @@ nsresult nsHttpChannel::OnBeforeConnect() { nsContentUtils::GetSecurityManager()->GetChannelResultPrincipal( this, getter_AddRefs(resultPrincipal)); } - OriginAttributes originAttributes; - if (!StoragePrincipalHelper::GetOriginAttributesForNetworkState( - this, originAttributes)) { - return NS_ERROR_FAILURE; - } // At this point it is no longer possible to call // HttpBaseChannel::UpgradeToSecure. mUpgradableToSecure = false; bool shouldUpgrade = mUpgradeToSecure; if (mURI->SchemeIs("http")) { + OriginAttributes originAttributes; + if (!StoragePrincipalHelper::GetOriginAttributesForNetworkState( + this, originAttributes)) { + return NS_ERROR_FAILURE; + } + if (!shouldUpgrade) { // Make sure http channel is released on main thread. // See bug 1539148 for details. @@ -2274,7 +2275,7 @@ nsresult nsHttpChannel::ProcessSingleSecurityHeader( // Process header will now discard the headers itself if the channel // wasn't secure (whereas before it had to be checked manually) OriginAttributes originAttributes; - if (NS_WARN_IF(!StoragePrincipalHelper::GetOriginAttributesForNetworkState( + if (NS_WARN_IF(!StoragePrincipalHelper::GetOriginAttributesForHSTS( this, originAttributes))) { return NS_ERROR_FAILURE; } diff --git a/toolkit/components/antitracking/StoragePrincipalHelper.cpp b/toolkit/components/antitracking/StoragePrincipalHelper.cpp index edf043eb3f7e..2f55bc478936 100644 --- a/toolkit/components/antitracking/StoragePrincipalHelper.cpp +++ b/toolkit/components/antitracking/StoragePrincipalHelper.cpp @@ -328,4 +328,42 @@ void StoragePrincipalHelper::UpdateOriginAttributesForNetworkState( aAttributes.SetPartitionKey(aFirstPartyURI); } +// static +bool StoragePrincipalHelper::GetOriginAttributesForHSTS( + nsIChannel* aChannel, OriginAttributes& aAttributes) { + if (!GetOriginAttributesForNetworkState(aChannel, aAttributes)) { + return false; + } + + if (aAttributes.mPartitionKey.IsEmpty() || + aAttributes.mPartitionKey[0] != '(') { + return true; + } + + nsAString::const_iterator start, end; + aAttributes.mPartitionKey.BeginReading(start); + aAttributes.mPartitionKey.EndReading(end); + + MOZ_DIAGNOSTIC_ASSERT(*start == '('); + start++; + + nsAString::const_iterator iter(start); + bool ok = FindCharInReadable(',', iter, end); + MOZ_DIAGNOSTIC_ASSERT(ok); + + nsAutoString scheme; + scheme.Assign(Substring(start, iter)); + + if (!scheme.EqualsLiteral("https")) { + return true; + } + + nsAutoString key; + key.AssignLiteral("(http"); + key.Append(Substring(iter, end)); + aAttributes.SetPartitionKey(key); + + return true; +} + } // namespace mozilla diff --git a/toolkit/components/antitracking/StoragePrincipalHelper.h b/toolkit/components/antitracking/StoragePrincipalHelper.h index 143433e88708..bfce7baeb0aa 100644 --- a/toolkit/components/antitracking/StoragePrincipalHelper.h +++ b/toolkit/components/antitracking/StoragePrincipalHelper.h @@ -268,6 +268,10 @@ class StoragePrincipalHelper final { OriginAttributes& aAttributes); static void UpdateOriginAttributesForNetworkState( nsIURI* aFirstPartyURI, OriginAttributes& aAttributes); + + // For HSTS we want to force 'HTTP' in the partition key. + static bool GetOriginAttributesForHSTS(nsIChannel* aChannel, + OriginAttributes& aAttributes); }; } // namespace mozilla diff --git a/toolkit/components/antitracking/test/browser/browser_staticPartition_HSTS.js b/toolkit/components/antitracking/test/browser/browser_staticPartition_HSTS.js index e87a09892744..1c34c2da3d1f 100644 --- a/toolkit/components/antitracking/test/browser/browser_staticPartition_HSTS.js +++ b/toolkit/components/antitracking/test/browser/browser_staticPartition_HSTS.js @@ -27,47 +27,61 @@ function promiseTabLoadEvent(aTab, aURL, aFinalURL) { return BrowserTestUtils.browserLoaded(aTab.linkedBrowser, false, aFinalURL); } -add_task(async function() { - for (let prefValue of [true, false]) { - await SpecialPowers.pushPrefEnv({ - set: [["privacy.partition.network_state", prefValue]], - }); - - let tab = (gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser)); - - // Let's load the secureURL as first-party in order to activate HSTS. - await promiseTabLoadEvent(tab, secureURL, secureURL); - - // Let's test HSTS: unsecure -> secure. - await promiseTabLoadEvent(tab, unsecureURL, secureURL); - ok(true, "unsecure -> secure, first-party works!"); - - // Let's load a first-party. - await promiseTabLoadEvent(tab, unsecureEmptyURL, unsecureEmptyURL); - - let finalURL = await SpecialPowers.spawn( - tab.linkedBrowser, - [unsecureURL], - async url => { - return new content.Promise(resolve => { - let ifr = content.document.createElement("iframe"); - ifr.onload = _ => { - resolve(ifr.contentWindow.location.href); - }; - - content.document.body.appendChild(ifr); - ifr.src = url; - }); +function waitFor(host) { + return new Promise(resolve => { + const observer = channel => { + if ( + channel instanceof Ci.nsIHttpChannel && + channel.URI.host === host && + channel.loadInfo.internalContentPolicyType === + Ci.nsIContentPolicy.TYPE_INTERNAL_IFRAME + ) { + Services.obs.removeObserver(observer, "http-on-stop-request"); + resolve(channel.URI.spec); } - ); + }; + Services.obs.addObserver(observer, "http-on-stop-request"); + }); +} - if (prefValue) { - is(finalURL, unsecureURL, "HSTS doesn't work for 3rd parties"); - } else { - is(finalURL, secureURL, "HSTS works for 3rd parties"); +add_task(async function() { + for (let networkIsolation of [true, false]) { + for (let partitionPerSite of [true, false]) { + await SpecialPowers.pushPrefEnv({ + set: [ + ["privacy.partition.network_state", networkIsolation], + ["privacy.dynamic_firstparty.use_site", partitionPerSite], + ], + }); + + let tab = (gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser)); + + // Let's load the secureURL as first-party in order to activate HSTS. + await promiseTabLoadEvent(tab, secureURL, secureURL); + + // Let's test HSTS: unsecure -> secure. + await promiseTabLoadEvent(tab, unsecureURL, secureURL); + ok(true, "unsecure -> secure, first-party works!"); + + // Let's load a first-party. + await promiseTabLoadEvent(tab, unsecureEmptyURL, unsecureEmptyURL); + + let finalURL = waitFor("example.com"); + + await SpecialPowers.spawn(tab.linkedBrowser, [unsecureURL], async url => { + let ifr = content.document.createElement("iframe"); + content.document.body.appendChild(ifr); + ifr.src = url; + }); + + if (networkIsolation) { + is(await finalURL, unsecureURL, "HSTS doesn't work for 3rd parties"); + } else { + is(await finalURL, secureURL, "HSTS works for 3rd parties"); + } + + gBrowser.removeCurrentTab(); + cleanupHSTS(); } - - gBrowser.removeCurrentTab(); - cleanupHSTS(); } });