Bug 1731778 - Implement COEP: credentialless r=smaug,necko-reviewers,kershaw

Spec: https://html.spec.whatwg.org/multipage/#coep:coep-credentialless

Credentialless is a new cross-origin embedder policy which allows us
to not enforcing CORP when loading cross-origin resources while
providing SharedArrayBuffer.

There are two main things involved here:
  1. Fetching cross-origin no-CORS resources omits credentials
     - This is done by applying `LOAD_ANONYMOUS` flag to the request
  2. Other requests sent with credentials require the server's explicit
  permission through the CORS protocol or the CORS header
     - This is done by expanding `ProcessCrossOriginResourcePolicyHeader`
     function to apply the necessary checks.

Differential Revision: https://phabricator.services.mozilla.com/D147802
This commit is contained in:
Sean Feng 2022-06-27 17:07:15 +00:00
parent 20e4c096c5
commit 6acee53f6c
12 changed files with 108 additions and 15 deletions

View File

@ -1513,6 +1513,10 @@ nsresult nsContentSecurityManager::CheckChannel(nsIChannel* aChannel) {
AddLoadFlags(aChannel, nsIRequest::LOAD_ANONYMOUS);
}
if (!CrossOriginEmbedderPolicyAllowsCredentials(aChannel)) {
AddLoadFlags(aChannel, nsIRequest::LOAD_ANONYMOUS);
}
nsSecurityFlags securityMode = loadInfo->GetSecurityMode();
// CORS mode is handled by nsCORSListenerProxy
@ -1562,6 +1566,54 @@ nsresult nsContentSecurityManager::CheckChannel(nsIChannel* aChannel) {
return NS_OK;
}
// https://fetch.spec.whatwg.org/#ref-for-cross-origin-embedder-policy-allows-credentials
bool nsContentSecurityManager::CrossOriginEmbedderPolicyAllowsCredentials(
nsIChannel* aChannel) {
nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
// 1. If requests mode is not "no-cors", then return true.
//
// `no-cors` check applies to document navigation such that if it is
// an document navigation, this check should return true to allow
// credentials.
if (loadInfo->GetExternalContentPolicyType() ==
ExtContentPolicy::TYPE_DOCUMENT ||
loadInfo->GetExternalContentPolicyType() ==
ExtContentPolicy::TYPE_SUBDOCUMENT) {
return true;
}
if (loadInfo->GetSecurityMode() !=
nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL &&
loadInfo->GetSecurityMode() !=
nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_INHERITS_SEC_CONTEXT) {
return true;
}
// If requests clients policy containers embedder policys value is not
// "credentialless", then return true.
if (loadInfo->GetLoadingEmbedderPolicy() !=
nsILoadInfo::EMBEDDER_POLICY_CREDENTIALLESS) {
return true;
}
// If requests origin is same origin with requests current URLs origin and
// request does not have a redirect-tainted origin, then return true.
nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
nsCOMPtr<nsIPrincipal> resourcePrincipal;
ssm->GetChannelURIPrincipal(aChannel, getter_AddRefs(resourcePrincipal));
bool sameOrigin = resourcePrincipal->Equals(loadInfo->TriggeringPrincipal());
nsAutoCString serializedOrigin;
GetSerializedOrigin(loadInfo->TriggeringPrincipal(), resourcePrincipal,
serializedOrigin, loadInfo);
if (sameOrigin && !serializedOrigin.IsEmpty()) {
return true;
}
return false;
}
// https://fetch.spec.whatwg.org/#serializing-a-request-origin
void nsContentSecurityManager::GetSerializedOrigin(
nsIPrincipal* aOrigin, nsIPrincipal* aResourceOrigin,

View File

@ -81,6 +81,7 @@ class nsContentSecurityManager : public nsIContentSecurityManager,
static nsresult CheckAllowLoadInSystemPrivilegedContext(nsIChannel* aChannel);
static nsresult CheckAllowLoadInPrivilegedAboutContext(nsIChannel* aChannel);
static nsresult CheckChannelHasProtocolSecurityFlag(nsIChannel* aChannel);
static bool CrossOriginEmbedderPolicyAllowsCredentials(nsIChannel* aChannel);
virtual ~nsContentSecurityManager() = default;
};

View File

@ -916,7 +916,8 @@ struct CrossOriginEmbedderPolicyValidator {
static bool IsLegalValue(const IntegralType e) {
return AreIntegralValuesEqual(e, nsILoadInfo::EMBEDDER_POLICY_NULL) ||
AreIntegralValuesEqual(e, nsILoadInfo::EMBEDDER_POLICY_REQUIRE_CORP);
AreIntegralValuesEqual(e, nsILoadInfo::EMBEDDER_POLICY_REQUIRE_CORP) ||
AreIntegralValuesEqual(e, nsILoadInfo::EMBEDDER_POLICY_CREDENTIALLESS);
}
private:

View File

@ -1517,6 +1517,13 @@
value: true
mirror: always
# This pref makes `credentialless` a valid value for
# Cross-Origin-Embedder-Policy header
- name: browser.tabs.remote.coep.credentialless
type: RelaxedAtomicBool
value: @IS_NIGHTLY_BUILD@
mirror: always
# When this pref is enabled top level loads with a mismatched
# Cross-Origin-Opener-Policy header will be loaded in a separate process.
- name: browser.tabs.remote.useCrossOriginOpenerPolicy

View File

@ -1380,6 +1380,7 @@ interface nsILoadInfo : nsISupports
cenum CrossOriginEmbedderPolicy : 8 {
EMBEDDER_POLICY_NULL = 0,
EMBEDDER_POLICY_REQUIRE_CORP = 1,
EMBEDDER_POLICY_CREDENTIALLESS = 2,
};
/**

View File

@ -2626,9 +2626,14 @@ NS_GetCrossOriginEmbedderPolicyFromHeader(const nsACString& aHeader) {
return nsILoadInfo::EMBEDDER_POLICY_NULL;
}
return embedderPolicy.EqualsLiteral("require-corp")
? nsILoadInfo::EMBEDDER_POLICY_REQUIRE_CORP
: nsILoadInfo::EMBEDDER_POLICY_NULL;
if (embedderPolicy.EqualsLiteral("require-corp")) {
return nsILoadInfo::EMBEDDER_POLICY_REQUIRE_CORP;
} else if (embedderPolicy.EqualsLiteral("credentialless") &&
StaticPrefs::browser_tabs_remote_coep_credentialless()) {
return nsILoadInfo::EMBEDDER_POLICY_CREDENTIALLESS;
}
return nsILoadInfo::EMBEDDER_POLICY_NULL;
}
/** Given the first (disposition) token from a Content-Disposition header,

View File

@ -2401,12 +2401,13 @@ nsresult HttpBaseChannel::ProcessCrossOriginEmbedderPolicyHeader() {
return NS_OK;
}
// https://mikewest.github.io/corpp/#abstract-opdef-process-navigation-response
// https://html.spec.whatwg.org/multipage/origin.html#coep
if (mLoadInfo->GetExternalContentPolicyType() ==
ExtContentPolicy::TYPE_SUBDOCUMENT &&
mLoadInfo->GetLoadingEmbedderPolicy() !=
nsILoadInfo::EMBEDDER_POLICY_NULL &&
resultPolicy != nsILoadInfo::EMBEDDER_POLICY_REQUIRE_CORP) {
resultPolicy != nsILoadInfo::EMBEDDER_POLICY_REQUIRE_CORP &&
resultPolicy != nsILoadInfo::EMBEDDER_POLICY_CREDENTIALLESS) {
return NS_ERROR_DOM_COEP_FAILED;
}
@ -2458,13 +2459,29 @@ nsresult HttpBaseChannel::ProcessCrossOriginResourcePolicyHeader() {
content);
if (StaticPrefs::browser_tabs_remote_useCrossOriginEmbedderPolicy()) {
// COEP 3.2.1.6 If policy is null, and embedder policy is "require-corp",
// set policy to "same-origin".
// Note that we treat invalid value as "cross-origin", which spec
// indicates. We might want to make that stricter.
if (content.IsEmpty() && mLoadInfo->GetLoadingEmbedderPolicy() ==
nsILoadInfo::EMBEDDER_POLICY_REQUIRE_CORP) {
content = "same-origin"_ns;
if (content.IsEmpty()) {
if (mLoadInfo->GetLoadingEmbedderPolicy() ==
nsILoadInfo::EMBEDDER_POLICY_CREDENTIALLESS &&
StaticPrefs::browser_tabs_remote_coep_credentialless()) {
bool requestIncludesCredentials = false;
nsresult rv = GetCorsIncludeCredentials(&requestIncludesCredentials);
if (NS_FAILED(rv)) {
return NS_OK;
}
// COEP: Set policy to `same-origin` if: responses
// request-includes-credentials is true, or forNavigation is true.
if (requestIncludesCredentials ||
extContentPolicyType == ExtContentPolicyType::TYPE_SUBDOCUMENT) {
content = "same-origin"_ns;
}
} else if (mLoadInfo->GetLoadingEmbedderPolicy() ==
nsILoadInfo::EMBEDDER_POLICY_REQUIRE_CORP) {
// COEP 3.2.1.6 If policy is null, and embedder policy is
// "require-corp", set policy to "same-origin". Note that we treat
// invalid value as "cross-origin", which spec indicates. We might want
// to make that stricter.
content = "same-origin"_ns;
}
}
}
@ -5726,7 +5743,8 @@ NS_IMETHODIMP HttpBaseChannel::ComputeCrossOriginOpenerPolicy(
nsILoadInfo::CrossOriginEmbedderPolicy coep =
nsILoadInfo::EMBEDDER_POLICY_NULL;
if (NS_SUCCEEDED(GetResponseEmbedderPolicy(&coep)) &&
coep == nsILoadInfo::EMBEDDER_POLICY_REQUIRE_CORP) {
(coep == nsILoadInfo::EMBEDDER_POLICY_REQUIRE_CORP ||
coep == nsILoadInfo::EMBEDDER_POLICY_CREDENTIALLESS)) {
policy =
nsILoadInfo::OPENER_POLICY_SAME_ORIGIN_EMBEDDER_POLICY_REQUIRE_CORP;
}

View File

@ -1 +1 @@
disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1698587
prefs: [browser.tabs.remote.useCrossOriginEmbedderPolicy:true, browser.tabs.remote.coep.credentialless:true]

View File

@ -0,0 +1,2 @@
[reporting-navigation.https.window.html]
disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1652926

View File

@ -0,0 +1,2 @@
[reporting-subresource-corp.https.window.html]
disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1652926

View File

@ -0,0 +1,2 @@
[service-worker-coep-credentialless-proxy.https.window.html]
disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1613912

View File

@ -0,0 +1,2 @@
[service-worker-coep-none-proxy.https.window.html]
disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1613912