mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-27 06:43:32 +00:00
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
0a3eef2b82
commit
487589caae
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
}
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user