forked from mirrors/gecko-dev
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:
parent
03a46b29e8
commit
60b6161a74
5 changed files with 105 additions and 45 deletions
|
|
@ -28,6 +28,9 @@ static void MakeTopLevelInfo(const nsACString& aScheme, const nsACString& aHost,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Note: If you change the serialization of the partition-key, please update
|
||||||
|
// StoragePrincipalHelper.cpp too.
|
||||||
|
|
||||||
nsAutoCString site;
|
nsAutoCString site;
|
||||||
site.AssignLiteral("(");
|
site.AssignLiteral("(");
|
||||||
site.Append(aScheme);
|
site.Append(aScheme);
|
||||||
|
|
|
||||||
|
|
@ -586,17 +586,18 @@ nsresult nsHttpChannel::OnBeforeConnect() {
|
||||||
nsContentUtils::GetSecurityManager()->GetChannelResultPrincipal(
|
nsContentUtils::GetSecurityManager()->GetChannelResultPrincipal(
|
||||||
this, getter_AddRefs(resultPrincipal));
|
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
|
// At this point it is no longer possible to call
|
||||||
// HttpBaseChannel::UpgradeToSecure.
|
// HttpBaseChannel::UpgradeToSecure.
|
||||||
mUpgradableToSecure = false;
|
mUpgradableToSecure = false;
|
||||||
bool shouldUpgrade = mUpgradeToSecure;
|
bool shouldUpgrade = mUpgradeToSecure;
|
||||||
if (mURI->SchemeIs("http")) {
|
if (mURI->SchemeIs("http")) {
|
||||||
|
OriginAttributes originAttributes;
|
||||||
|
if (!StoragePrincipalHelper::GetOriginAttributesForNetworkState(
|
||||||
|
this, originAttributes)) {
|
||||||
|
return NS_ERROR_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
if (!shouldUpgrade) {
|
if (!shouldUpgrade) {
|
||||||
// Make sure http channel is released on main thread.
|
// Make sure http channel is released on main thread.
|
||||||
// See bug 1539148 for details.
|
// See bug 1539148 for details.
|
||||||
|
|
@ -2273,7 +2274,7 @@ nsresult nsHttpChannel::ProcessSingleSecurityHeader(
|
||||||
// Process header will now discard the headers itself if the channel
|
// Process header will now discard the headers itself if the channel
|
||||||
// wasn't secure (whereas before it had to be checked manually)
|
// wasn't secure (whereas before it had to be checked manually)
|
||||||
OriginAttributes originAttributes;
|
OriginAttributes originAttributes;
|
||||||
if (NS_WARN_IF(!StoragePrincipalHelper::GetOriginAttributesForNetworkState(
|
if (NS_WARN_IF(!StoragePrincipalHelper::GetOriginAttributesForHSTS(
|
||||||
this, originAttributes))) {
|
this, originAttributes))) {
|
||||||
return NS_ERROR_FAILURE;
|
return NS_ERROR_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -328,4 +328,42 @@ void StoragePrincipalHelper::UpdateOriginAttributesForNetworkState(
|
||||||
aAttributes.SetPartitionKey(aFirstPartyURI);
|
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
|
} // namespace mozilla
|
||||||
|
|
|
||||||
|
|
@ -268,6 +268,10 @@ class StoragePrincipalHelper final {
|
||||||
OriginAttributes& aAttributes);
|
OriginAttributes& aAttributes);
|
||||||
static void UpdateOriginAttributesForNetworkState(
|
static void UpdateOriginAttributesForNetworkState(
|
||||||
nsIURI* aFirstPartyURI, OriginAttributes& aAttributes);
|
nsIURI* aFirstPartyURI, OriginAttributes& aAttributes);
|
||||||
|
|
||||||
|
// For HSTS we want to force 'HTTP' in the partition key.
|
||||||
|
static bool GetOriginAttributesForHSTS(nsIChannel* aChannel,
|
||||||
|
OriginAttributes& aAttributes);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace mozilla
|
} // namespace mozilla
|
||||||
|
|
|
||||||
|
|
@ -27,47 +27,61 @@ function promiseTabLoadEvent(aTab, aURL, aFinalURL) {
|
||||||
return BrowserTestUtils.browserLoaded(aTab.linkedBrowser, false, aFinalURL);
|
return BrowserTestUtils.browserLoaded(aTab.linkedBrowser, false, aFinalURL);
|
||||||
}
|
}
|
||||||
|
|
||||||
add_task(async function() {
|
function waitFor(host) {
|
||||||
for (let prefValue of [true, false]) {
|
return new Promise(resolve => {
|
||||||
await SpecialPowers.pushPrefEnv({
|
const observer = channel => {
|
||||||
set: [["privacy.partition.network_state", prefValue]],
|
if (
|
||||||
});
|
channel instanceof Ci.nsIHttpChannel &&
|
||||||
|
channel.URI.host === host &&
|
||||||
let tab = (gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser));
|
channel.loadInfo.internalContentPolicyType ===
|
||||||
|
Ci.nsIContentPolicy.TYPE_INTERNAL_IFRAME
|
||||||
// Let's load the secureURL as first-party in order to activate HSTS.
|
) {
|
||||||
await promiseTabLoadEvent(tab, secureURL, secureURL);
|
Services.obs.removeObserver(observer, "http-on-stop-request");
|
||||||
|
resolve(channel.URI.spec);
|
||||||
// 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;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
);
|
};
|
||||||
|
Services.obs.addObserver(observer, "http-on-stop-request");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (prefValue) {
|
add_task(async function() {
|
||||||
is(finalURL, unsecureURL, "HSTS doesn't work for 3rd parties");
|
for (let networkIsolation of [true, false]) {
|
||||||
} else {
|
for (let partitionPerSite of [true, false]) {
|
||||||
is(finalURL, secureURL, "HSTS works for 3rd parties");
|
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();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue