Bug 1635828 - Isolate HSTS per first-party when privacy.partition.network_state is set to true - part 3 - partition key, r=xeonchen

Differential Revision: https://phabricator.services.mozilla.com/D83316
This commit is contained in:
Andrea Marchesini 2020-09-16 14:33:31 +00:00
parent 0a3eef2b82
commit 487589caae
5 changed files with 105 additions and 45 deletions

View File

@ -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);

View File

@ -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;
}

View File

@ -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

View File

@ -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

View File

@ -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();
}
});