зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1830070: Correctly apply RFP Checks to about: documents and deal with pop-ups r=smaug,necko-reviewers,emilio
This patch has three parts to it: 1) Use NS_IsContentAccessibleAboutURI to ensure that only safe about: documents get exempted. With this change, we will no longer allow about:blank or about:srcdoc to be exempted base on URI. If they are to be exempted, it will need to be base on other information. 2) In Document::RecomputeResistFingerprinting we previously deferred to a Parent Document if we had one, and either the principals matched or we were a null principal. We will do the same thing, except we will also defer to our opener as well as the parent document. Now about:blank documents can be exempted. However, this deferral only works if the opener is same-process. For cross-process openers, we make the decision ourselves. We can make the wrong decision though. CookieJarSettings is inherited through iframes but it is _not_ inherited through popups. (Yet. There's some discussion there, but it's not implemented.) Conceptually; however, we do want CJS to inherit, and we do want RFP to inherit as well. Because a popup can collude with its opener to bypass RFP and Storage restrictions, we should propagate the CJS information. This does lead to an unusual situation: if you have exempted b.com, and a.com (which is not exempted) creates a popup for b.com then that popup will not be exempted. But an open tab for b.com would be. And it might be hard to tell those two apart, or why they behave differently. The third part of the patch: 3) In LoadInfo we want to populate information down from the opener to the popup. This is needed because otherwise a cross-origin popup will not defer to its opener (because in Fission they're in different processes) and will decide if it should be exempted itself. It's the CookieJarSettings object that prevents the cross-origin document from thinking it should be exempted - CJS tells it 'No, you're a child (either a subdocument or a popup) and if I say you don't get an exemption, you don't.' Finally, there is one more caveat: we can only defer to a parent document or opener if it still exists. A popup may outlive its opener. If that happens, and something induces a call to RecomputeResistFingerprinting, then (e.g.) an about:blank popup may lose an RFP exemption that it had received from its parent. This isn't expected to happen in practice - RecomputeResistFingerprinting is only called on document creation and pref changes I believe. It is not possible for a popup to _gain_ an exemption though, because even if the parent document is gone, the CJS lives on and restricts it. Differential Revision: https://phabricator.services.mozilla.com/D178866
This commit is contained in:
Родитель
818cec0c8f
Коммит
bc2e6a72ad
|
@ -59,4 +59,4 @@ add_task(testA.bind(null, uri, testHWConcurrency, expectedResults));
|
|||
|
||||
// (E) RFP is not exempted on the popup maker
|
||||
expectedResults = structuredClone(allSpoofed);
|
||||
add_task(testE.bind(null, uri, testHWConcurrency, expectedResults));
|
||||
add_task(testE.bind(null, uri, testHWConcurrency, expectedResults));
|
||||
|
|
|
@ -1107,6 +1107,25 @@ BasePrincipal::IsURIInList(const nsACString& aList, bool* aResult) {
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
BasePrincipal::IsContentAccessibleAboutURI(bool* aResult) {
|
||||
*aResult = false;
|
||||
|
||||
if (NS_FAILED(SchemeIs("about", aResult)) || !aResult) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIURI> prinURI;
|
||||
|
||||
nsresult rv = GetURI(getter_AddRefs(prinURI));
|
||||
if (NS_FAILED(rv) || !prinURI) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
*aResult = NS_IsContentAccessibleAboutURI(prinURI);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
BasePrincipal::GetIsOriginPotentiallyTrustworthy(bool* aResult) {
|
||||
AssertIsOnMainThread();
|
||||
|
|
|
@ -137,6 +137,7 @@ class BasePrincipal : public nsJSPrincipals {
|
|||
NS_IMETHOD SchemeIs(const char* aScheme, bool* aResult) override;
|
||||
NS_IMETHOD IsURIInPrefList(const char* aPref, bool* aResult) override;
|
||||
NS_IMETHOD IsURIInList(const nsACString& aList, bool* aResult) override;
|
||||
NS_IMETHOD IsContentAccessibleAboutURI(bool* aResult) override;
|
||||
NS_IMETHOD IsL10nAllowed(nsIURI* aURI, bool* aResult) override;
|
||||
NS_IMETHOD GetAboutModuleFlags(uint32_t* flags) override;
|
||||
NS_IMETHOD GetIsAddonOrExpandedAddonPrincipal(bool* aResult) override;
|
||||
|
|
|
@ -394,6 +394,14 @@ interface nsIPrincipal : nsISupports
|
|||
[infallible]
|
||||
boolean isURIInList(in ACString list);
|
||||
|
||||
/**
|
||||
* Check if the Principal's URI is a content-accessible about: page
|
||||
*
|
||||
* May be called from any thread.
|
||||
*/
|
||||
[infallible]
|
||||
boolean isContentAccessibleAboutURI();
|
||||
|
||||
/**
|
||||
* Uses NS_Security Compare to determine if the
|
||||
* other URI is same-origin as the uri of the Principal
|
||||
|
|
|
@ -16164,17 +16164,26 @@ void Document::SendPageUseCounters() {
|
|||
bool Document::RecomputeResistFingerprinting() {
|
||||
const bool previous = mShouldResistFingerprinting;
|
||||
|
||||
if (mParentDocument &&
|
||||
(NodePrincipal()->Equals(mParentDocument->NodePrincipal()) ||
|
||||
NodePrincipal()->GetIsNullPrincipal())) {
|
||||
// If we have a parent document, defer to it only when we have a null
|
||||
// principal (e.g. a sandboxed iframe or a data: uri) or when the parent
|
||||
// document's principal matches. This means we will defer about:blank,
|
||||
// about:srcdoc, blob and same-origin iframes to the parent, but not
|
||||
// cross-origin iframes.
|
||||
mShouldResistFingerprinting = !nsContentUtils::IsChromeDoc(this) &&
|
||||
mParentDocument->ShouldResistFingerprinting(
|
||||
RFPTarget::IsAlwaysEnabledForPrecompute);
|
||||
RefPtr<BrowsingContext> opener =
|
||||
GetBrowsingContext() ? GetBrowsingContext()->GetOpener() : nullptr;
|
||||
// If we have a parent or opener document, defer to it only when we have a
|
||||
// null principal (e.g. a sandboxed iframe or a data: uri) or when the
|
||||
// document's principal matches. This means we will defer about:blank,
|
||||
// about:srcdoc, blob and same-origin iframes/popups to the parent/opener,
|
||||
// but not cross-origin ones. Cross-origin iframes/popups may inherit a
|
||||
// CookieJarSettings.mShouldRFP = false bit however, which will be respected.
|
||||
auto shouldInheritFrom = [this](Document* aDoc) {
|
||||
return aDoc && (this->NodePrincipal()->Equals(aDoc->NodePrincipal()) ||
|
||||
this->NodePrincipal()->GetIsNullPrincipal());
|
||||
};
|
||||
|
||||
if (shouldInheritFrom(mParentDocument)) {
|
||||
mShouldResistFingerprinting = mParentDocument->ShouldResistFingerprinting(
|
||||
RFPTarget::IsAlwaysEnabledForPrecompute);
|
||||
} else if (opener && shouldInheritFrom(opener->GetDocument())) {
|
||||
mShouldResistFingerprinting =
|
||||
opener->GetDocument()->ShouldResistFingerprinting(
|
||||
RFPTarget::IsAlwaysEnabledForPrecompute);
|
||||
} else {
|
||||
mShouldResistFingerprinting =
|
||||
!nsContentUtils::IsChromeDoc(this) &&
|
||||
|
|
|
@ -2166,6 +2166,7 @@ bool nsContentUtils::ShouldResistFingerprinting(nsIGlobalObject* aGlobalObject,
|
|||
}
|
||||
|
||||
// Newer Should RFP Functions ----------------------------------
|
||||
// Utilities ---------------------------------------------------
|
||||
|
||||
inline void LogDomainAndPrefList(const char* exemptedDomainsPrefName,
|
||||
nsAutoCString& url, bool isExemptDomain) {
|
||||
|
@ -2200,9 +2201,29 @@ inline bool CookieJarSettingsSaysShouldResistFingerprinting(
|
|||
return cookieJarSettings->GetShouldResistFingerprinting();
|
||||
}
|
||||
|
||||
inline bool SchemeSaysShouldNotResistFingerprinting(nsIURI* aURI) {
|
||||
return aURI->SchemeIs("chrome") || aURI->SchemeIs("resource") ||
|
||||
aURI->SchemeIs("view-source") || aURI->SchemeIs("moz-extension") ||
|
||||
(aURI->SchemeIs("about") && !NS_IsContentAccessibleAboutURI(aURI));
|
||||
}
|
||||
|
||||
inline bool SchemeSaysShouldNotResistFingerprinting(nsIPrincipal* aPrincipal) {
|
||||
if (aPrincipal->SchemeIs("chrome") || aPrincipal->SchemeIs("resource") ||
|
||||
aPrincipal->SchemeIs("view-source") ||
|
||||
aPrincipal->SchemeIs("moz-extension")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool isSpecialAboutURI;
|
||||
Unused << aPrincipal->IsContentAccessibleAboutURI(&isSpecialAboutURI);
|
||||
return isSpecialAboutURI;
|
||||
}
|
||||
|
||||
const char* kExemptedDomainsPrefName =
|
||||
"privacy.resistFingerprinting.exemptedDomains";
|
||||
|
||||
// Functions ---------------------------------------------------
|
||||
|
||||
/* static */
|
||||
bool nsContentUtils::ShouldResistFingerprinting(const char* aJustification,
|
||||
RFPTarget aTarget) {
|
||||
|
@ -2340,9 +2361,7 @@ bool nsContentUtils::ShouldResistFingerprinting_dangerous(
|
|||
}
|
||||
|
||||
// Exclude internal schemes and web extensions
|
||||
if (aURI->SchemeIs("about") || aURI->SchemeIs("chrome") ||
|
||||
aURI->SchemeIs("resource") || aURI->SchemeIs("view-source") ||
|
||||
aURI->SchemeIs("moz-extension")) {
|
||||
if (SchemeSaysShouldNotResistFingerprinting(aURI)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -2423,9 +2442,8 @@ bool nsContentUtils::ShouldResistFingerprinting_dangerous(
|
|||
}
|
||||
}
|
||||
|
||||
// Exclude internal schemes
|
||||
if (aPrincipal->SchemeIs("about") || aPrincipal->SchemeIs("chrome") ||
|
||||
aPrincipal->SchemeIs("resource") || aPrincipal->SchemeIs("view-source")) {
|
||||
// Exclude internal schemes and web extensions
|
||||
if (SchemeSaysShouldNotResistFingerprinting(aPrincipal)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -2453,18 +2471,18 @@ bool nsContentUtils::ShouldResistFingerprinting_dangerous(
|
|||
// So perform this last-ditch check for that scenario.
|
||||
// We arbitrarily use https as the scheme, but it doesn't matter.
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
nsresult rv;
|
||||
if (isExemptDomain && StaticPrefs::privacy_firstparty_isolate() &&
|
||||
!originAttributes.mFirstPartyDomain.IsEmpty()) {
|
||||
rv = NS_NewURI(getter_AddRefs(uri),
|
||||
u"https://"_ns + originAttributes.mFirstPartyDomain);
|
||||
nsresult rv =
|
||||
NS_NewURI(getter_AddRefs(uri),
|
||||
u"https://"_ns + originAttributes.mFirstPartyDomain);
|
||||
if (!NS_FAILED(rv)) {
|
||||
isExemptDomain =
|
||||
nsContentUtils::IsURIInPrefList(uri, kExemptedDomainsPrefName);
|
||||
}
|
||||
} else if (isExemptDomain && !originAttributes.mPartitionKey.IsEmpty()) {
|
||||
rv = NS_NewURI(getter_AddRefs(uri),
|
||||
u"https://"_ns + originAttributes.mPartitionKey);
|
||||
nsresult rv = NS_NewURI(getter_AddRefs(uri),
|
||||
u"https://"_ns + originAttributes.mPartitionKey);
|
||||
if (!NS_FAILED(rv)) {
|
||||
isExemptDomain =
|
||||
nsContentUtils::IsURIInPrefList(uri, kExemptedDomainsPrefName);
|
||||
|
@ -2474,6 +2492,8 @@ bool nsContentUtils::ShouldResistFingerprinting_dangerous(
|
|||
return !isExemptDomain;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
/* static */
|
||||
void nsContentUtils::CalcRoundedWindowSizeForResistingFingerprinting(
|
||||
int32_t aChromeWidth, int32_t aChromeHeight, int32_t aScreenWidth,
|
||||
|
|
|
@ -421,15 +421,27 @@ LoadInfo::LoadInfo(dom::CanonicalBrowsingContext* aBrowsingContext,
|
|||
}
|
||||
#endif
|
||||
|
||||
// Let's take the current cookie behavior and current cookie permission
|
||||
// for the documents' loadInfo. Note that for any other loadInfos,
|
||||
// cookieBehavior will be BEHAVIOR_REJECT for security reasons.
|
||||
bool isPrivate = mOriginAttributes.mPrivateBrowsingId > 0;
|
||||
// If we think we should not resist fingerprinting, defer to the opener's
|
||||
// RFP bit (if there is an opener.) If the opener is also exempted, it stays
|
||||
// true, otherwise we will put a false into the CJS and that will be respected
|
||||
// on this document.
|
||||
bool shouldResistFingerprinting =
|
||||
nsContentUtils::ShouldResistFingerprinting_dangerous(
|
||||
aURI, mOriginAttributes,
|
||||
"We are creating CookieJarSettings, so we can't have one already.",
|
||||
RFPTarget::IsAlwaysEnabledForPrecompute);
|
||||
RefPtr<BrowsingContext> opener = aBrowsingContext->GetOpener();
|
||||
if (!shouldResistFingerprinting && opener &&
|
||||
opener->GetCurrentWindowContext()) {
|
||||
shouldResistFingerprinting =
|
||||
opener->GetCurrentWindowContext()->ShouldResistFingerprinting();
|
||||
}
|
||||
|
||||
const bool isPrivate = mOriginAttributes.mPrivateBrowsingId > 0;
|
||||
|
||||
// Let's take the current cookie behavior and current cookie permission
|
||||
// for the documents' loadInfo. Note that for any other loadInfos,
|
||||
// cookieBehavior will be BEHAVIOR_REJECT for security reasons.
|
||||
mCookieJarSettings = CookieJarSettings::Create(
|
||||
isPrivate ? CookieJarSettings::ePrivate : CookieJarSettings::eRegular,
|
||||
shouldResistFingerprinting);
|
||||
|
|
|
@ -33,13 +33,13 @@ As you can see in the callgraph below, directly calling a *dangerous* function w
|
|||
SRFP_channel["ShouldResistFingerprinting(nsIChannel*)"]
|
||||
click SRFP_channel href "https://searchfox.org/mozilla-central/search?q=symbol:_ZN14nsContentUtils26ShouldResistFingerprintingEP10nsIChannelN7mozilla9RFPTargetE&redirect=false"
|
||||
|
||||
SRFP_uri["ShouldResistFingerprinting_dangerous(nsIURI*, OriginAttributes)<br />PBM Check<br />Scheme (inc WebExtension) Check<br />URI Exempt Check"]
|
||||
SRFP_uri["ShouldResistFingerprinting_dangerous(nsIURI*, OriginAttributes)<br />PBM Check<br />Scheme (inc WebExtension) Check<br />About Page Check<br />URI Exempt Check"]
|
||||
click SRFP_uri href "https://searchfox.org/mozilla-central/search?q=symbol:_ZN14nsContentUtils36ShouldResistFingerprinting_dangerousEP6nsIURIRKN7mozilla16OriginAttributesEPKcNS2_9RFPTargetE&redirect=false"
|
||||
|
||||
SRFP_loadinfo["ShouldResistFingerprinting(nsILoadInfo)<br />CookieJarSettingsSaysShouldResistFingerprinting Check<br />System Principal Check"]
|
||||
click SRFP_loadinfo href "https://searchfox.org/mozilla-central/search?q=symbol:_ZN14nsContentUtils26ShouldResistFingerprintingEP11nsILoadInfoN7mozilla9RFPTargetE&redirect=false"
|
||||
|
||||
SRFP_principal["ShouldResistFingerprinting_dangerous(nsIPrincipal*)<br />System Principal Check<br />PBM Check<br />Scheme Check<br />Web Extension Principal Check<br />URI Exempt Check"]
|
||||
SRFP_principal["ShouldResistFingerprinting_dangerous(nsIPrincipal*)<br />System Principal Check<br />PBM Check<br />Scheme Check<br />About Page Check<br />Web Extension Principal Check<br />URI Exempt Check"]
|
||||
click SRFP_principal href "https://searchfox.org/mozilla-central/search?q=symbol:_ZN14nsContentUtils36ShouldResistFingerprinting_dangerousEP12nsIPrincipalPKcN7mozilla9RFPTargetE&redirect=false"
|
||||
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче