Bug 1857894 - Recalculate referrer based on original referrer after non-HSTS HTTPS upgrade r=necko-reviewers,freddyb,kershaw

Per fetch spec [1], we should perform CSP upgrade-insecure-requests and mixed
content upgrades before determining the referrer, while HSTS upgrades happen
after the referrer is determined. In our implementation, we determine the
referrer before all the upgrades, so we need to recalculate the referrer
after we upgrade through anything but HSTS.

[1] https://fetch.spec.whatwg.org/#main-fetch

Differential Revision: https://phabricator.services.mozilla.com/D193417
This commit is contained in:
Malte Juergens 2024-01-17 09:36:53 +00:00
parent ad91a46064
commit 7c11f39184
7 changed files with 166 additions and 38 deletions

View File

@ -13,6 +13,15 @@ This Test is split into two for Bug 1453396
<script type="application/javascript">
// We do not want HTTPS upgrades to interfere with this test. For that,
// see dom/security/test/referrer-policy/test_referrer_redirect.html.
SpecialPowers.pushPrefEnv({
set: [
["dom.security.https_first", false],
["security.mixed_content.upgrade_display_content", false]
],
});
//generates URLs to test
var generateURLArray = (function(from, to){
const baseURL = '://example.com/tests/dom/base/test/bug704320.sjs?action=create-1st-level-iframe&scheme-from=';

View File

@ -18,6 +18,10 @@
const testCases = [
{ACTION: ["generate-link-policy-test"],
PREFS: [
["dom.security.https_first", false],
["security.mixed_content.upgrade_display_content", false]
],
TESTS: [
{ATTRIBUTE_POLICY: 'unsafe-url',
NAME: 'preload-unsafe-url-with-origin-in-meta',
@ -209,6 +213,76 @@
DESC: "preload-unsafe-url-property (orginally origin) with no-referrer in meta",
RESULT: 'full'},
]},
{
// All previos tests with SCHEME_FROM: 'https' and SCHEME_TO: 'http',
// this time with mixed content upgrading enabled.
ACTION: ["generate-link-policy-test"],
PREFS: [
["dom.security.https_first", false],
["security.mixed_content.upgrade_display_content", true]
],
TESTS: [
// Downgrade.
{ATTRIBUTE_POLICY: 'no-referrer-when-downgrade',
NAME: 'preload-origin-in-meta-downgrade-in-attr-upgraded',
META_POLICY: 'origin',
DESC: 'preload-origin in meta downgrade in attr (upgraded)',
REL: 'preload',
SCHEME_FROM: 'https',
SCHEME_TO: 'http',
RESULT: 'full'},
{ATTRIBUTE_POLICY: 'strict-origin',
NAME: 'preload-origin-in-meta-strict-origin-in-attr-upgraded',
META_POLICY: 'origin',
DESC: 'preload-origin in meta strict-origin in attr (upgraded)',
REL: 'preload',
SCHEME_FROM: 'https',
SCHEME_TO: 'http',
RESULT: 'origin'},
{ATTRIBUTE_POLICY: 'strict-origin-when-cross-origin',
NAME: 'preload-origin-in-meta-strict-origin-when-cross-origin-in-attr-upgraded',
META_POLICY: 'origin',
DESC: 'preload-origin in meta strict-origin-when-cross-origin in attr (upgraded)',
REL: 'preload',
SCHEME_FROM: 'https',
SCHEME_TO: 'http',
RESULT: 'full'},
// Cross origin
{ATTRIBUTE_POLICY: 'origin-when-cross-origin',
NAME: 'preload-origin-when-cross-origin-with-no-meta-upgraded',
META_POLICY: '',
SCHEME_FROM: 'https',
SCHEME_TO: 'http',
REL: 'preload',
DESC: "preload-origin-when-cross-origin with no meta (upgraded)",
RESULT: 'full'},
{ATTRIBUTE_POLICY: 'origin-when-cross-origin',
NAME: 'preload-origin-when-cross-origin-with-no-referrer-in-meta-upgraded',
META_POLICY: 'no-referrer',
SCHEME_FROM: 'https',
SCHEME_TO: 'http',
REL: 'preload',
DESC: "preload-origin-when-cross-origin with no-referrer in meta (upgraded)",
RESULT: 'full'},
{ATTRIBUTE_POLICY: 'origin-when-cross-origin',
NAME: 'preload-origin-when-cross-origin-with-unsafe-url-in-meta-upgraded-upgraded',
META_POLICY: 'unsafe-url',
SCHEME_FROM: 'https',
SCHEME_TO: 'http',
REL: 'preload',
DESC: "preload-origin-when-cross-origin with unsafe-url in meta (upgraded)",
RESULT: 'full'},
{ATTRIBUTE_POLICY: 'origin-when-cross-origin',
NAME: 'preload-origin-when-cross-origin-with-origin-in-meta-upgraded-upgraded',
META_POLICY: 'origin',
SCHEME_FROM: 'https',
SCHEME_TO: 'http',
REL: 'preload',
DESC: "preload-origin-when-cross-origin with origin in meta (upgraded)",
RESULT: 'full'},
]
}
];
</script>

View File

@ -1228,21 +1228,6 @@ ReferrerInfo::InitWithElement(const Element* aElement) {
return NS_OK;
}
/* static */
already_AddRefed<nsIReferrerInfo>
ReferrerInfo::CreateFromOtherAndPolicyOverride(
nsIReferrerInfo* aOther, ReferrerPolicyEnum aPolicyOverride) {
MOZ_ASSERT(aOther);
ReferrerPolicyEnum policy = aPolicyOverride != ReferrerPolicy::_empty
? aPolicyOverride
: aOther->ReferrerPolicy();
nsCOMPtr<nsIURI> referrer = aOther->GetComputedReferrer();
nsCOMPtr<nsIReferrerInfo> referrerInfo =
new ReferrerInfo(referrer, policy, aOther->GetSendReferrer());
return referrerInfo.forget();
}
/* static */
already_AddRefed<nsIReferrerInfo>
ReferrerInfo::CreateFromDocumentAndPolicyOverride(

View File

@ -93,16 +93,6 @@ class ReferrerInfo : public nsIReferrerInfo {
// Record the telemetry for the referrer policy.
void RecordTelemetry(nsIHttpChannel* aChannel);
/*
* Helper function to create a new ReferrerInfo object from other. We will not
* pass in any computed values and override referrer policy if needed
*
* @param aOther the other referrerInfo object to init from.
* @param aPolicyOverride referrer policy to override if necessary.
*/
static already_AddRefed<nsIReferrerInfo> CreateFromOtherAndPolicyOverride(
nsIReferrerInfo* aOther, ReferrerPolicyEnum aPolicyOverride);
/*
* Helper function to create a new ReferrerInfo object from a given document
* and override referrer policy if needed (for example, when parsing link

View File

@ -12,6 +12,7 @@ const BASE_URL = BASE_ORIGIN + SJS_PATH + SJS;
const SHARED_KEY = SJS;
const SAME_ORIGIN = "mochi.test:8888" + SJS_PATH + SJS;
const CROSS_ORIGIN_URL = "test1.example.com" + SJS_PATH + SJS;
const HSTS_URL = "includesubdomains.preloaded.test" + SJS_PATH + SJS;
const IMG_BYTES = atob(
"iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12" +
@ -266,7 +267,7 @@ function createTargetBlankRefferer(
}
// creates test page with img that is a redirect
function createRedirectImgTestCase(aParams, aAttributePolicy) {
function createImgTestCase(aParams, aAttributePolicy, aRedirect) {
var metaString = "";
if (aParams.has("META_POLICY")) {
metaString = `<meta name="referrer" content="${aParams.get(
@ -274,8 +275,16 @@ function createRedirectImgTestCase(aParams, aAttributePolicy) {
)}">`;
}
aParams.delete("ACTION");
aParams.append("ACTION", "redirectImg");
var imgUrl = "http://" + CROSS_ORIGIN_URL + aParams.toString();
if (aRedirect) {
aParams.append("ACTION", "redirectImg");
} else {
aParams.append("ACTION", "test");
aParams.append("type", "img");
}
var imgUrl =
"http://" +
(aParams.get("HSTS") ? HSTS_URL : CROSS_ORIGIN_URL) +
aParams.toString();
return `<!DOCTYPE HTML>
<html>
@ -637,7 +646,7 @@ function handleRequest(request, response) {
// redirect tests with img and iframe
if (action === "generate-img-redirect-policy-test") {
response.write(createRedirectImgTestCase(params, attributePolicy));
response.write(createImgTestCase(params, attributePolicy, true));
return;
}
if (action === "generate-iframe-redirect-policy-test") {
@ -655,6 +664,11 @@ function handleRequest(request, response) {
return;
}
if (action === "generate-img-policy-test") {
response.write(createImgTestCase(params, attributePolicy, false));
return;
}
_getPage = createLinkPageUsingRefferer.bind(
null,
metaPolicy,

View File

@ -14,7 +14,7 @@
<script type="application/javascript">
const SJS = "://example.com/tests/dom/security/test/referrer-policy/referrer_testserver.sjs?";
const PARAMS = ["ATTRIBUTE_POLICY", "NEW_ATTRIBUTE_POLICY", "META_POLICY", "RP_HEADER"];
const PARAMS = ["ATTRIBUTE_POLICY", "NEW_ATTRIBUTE_POLICY", "META_POLICY", "RP_HEADER", "HSTS"];
const testCases = [
{ACTION: ["generate-img-redirect-policy-test", "generate-iframe-redirect-policy-test"],
@ -111,6 +111,51 @@
RESULT: "origin"
}
]
},
// Check that "internal" redirects for mixed content upgrading
// are invisible, but not for HSTS upgrades (Bug 1857894).
{
ACTION: ["generate-img-policy-test"],
PREFS: [["security.mixed_content.upgrade_display_content", true]],
TESTS: [
{
META_POLICY: "strict-origin",
NAME: "img-strict-origin-mixed-content-upgrade",
DESC: "img-strict-origin-mixed-content-upgrade",
SCHEME_FROM: "https",
RESULT: "other-origin",
},
]
},
{
ACTION: ["generate-img-policy-test"],
PREFS: [["security.mixed_content.upgrade_display_content", false]],
TESTS: [
{
META_POLICY: "strict-origin",
NAME: "img-strict-origin-mixed-content-no-upgrade",
DESC: "img-strict-origin-mixed-content-no-upgrade",
SCHEME_FROM: "https",
RESULT: "none",
},
]
},
{
ACTION: ["generate-img-policy-test"],
PREFS: [
["security.mixed_content.upgrade_display_content", false],
["network.stricttransportsecurity.preloadlist", true],
],
TESTS: [
{
META_POLICY: "strict-origin",
NAME: "img-strict-origin-hsts-upgrade",
DESC: "img-strict-origin-hsts-upgrade",
SCHEME_FROM: "https",
RESULT: "none",
HSTS: true,
},
]
}
];
</script>

View File

@ -4561,14 +4561,25 @@ HttpBaseChannel::CloneReplacementChannelConfig(bool aPreserveMethod,
dom::ReferrerInfo::ReferrerPolicyFromHeaderString(tRPHeaderValue);
}
if (referrerPolicy != dom::ReferrerPolicy::_empty) {
// We may reuse computed referrer in redirect, so if referrerPolicy
// changes, we must not use the old computed value, and have to compute
// again.
nsCOMPtr<nsIReferrerInfo> referrerInfo =
dom::ReferrerInfo::CreateFromOtherAndPolicyOverride(mReferrerInfo,
referrerPolicy);
config.referrerInfo = referrerInfo;
// In case we are here because an upgrade happened through mixed content
// upgrading, CSP upgrade-insecure-requests, HTTPS-Only or HTTPS-First, we
// have to recalculate the referrer based on the original referrer to
// account for the different scheme. This does NOT apply to HSTS.
// See Bug 1857894 and order of https://fetch.spec.whatwg.org/#main-fetch.
// Otherwise, if we have a new referrer policy, we want to recalculate the
// referrer based on the old computed referrer (Bug 1678545).
bool wasNonHSTSUpgrade =
(aRedirectFlags & nsIChannelEventSink::REDIRECT_STS_UPGRADE) &&
(!mLoadInfo->GetHstsStatus());
if (wasNonHSTSUpgrade) {
nsCOMPtr<nsIURI> referrer = mReferrerInfo->GetOriginalReferrer();
config.referrerInfo =
new dom::ReferrerInfo(referrer, mReferrerInfo->ReferrerPolicy(),
mReferrerInfo->GetSendReferrer());
} else if (referrerPolicy != dom::ReferrerPolicy::_empty) {
nsCOMPtr<nsIURI> referrer = mReferrerInfo->GetComputedReferrer();
config.referrerInfo = new dom::ReferrerInfo(
referrer, referrerPolicy, mReferrerInfo->GetSendReferrer());
} else {
config.referrerInfo = mReferrerInfo;
}