From 5568524e05eb1693b5c69f6c0d402167e466d7fb Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Thu, 3 Dec 2020 00:56:22 +0000 Subject: [PATCH] Bug 1605305 - Origin header field is not set to HTTP request in cases where it is required, r=ckerschb,necko-reviewers,JuniorHsu,valentin *** *** Differential Revision: https://phabricator.services.mozilla.com/D80905 --- .../search/test/browser/mozsearch.sjs | 2 +- dom/fetch/FetchDriver.cpp | 12 --- dom/security/ReferrerInfo.cpp | 66 ++++++++----- dom/security/ReferrerInfo.h | 21 ++-- netwerk/protocol/http/HttpBaseChannel.cpp | 38 ++++++++ netwerk/protocol/http/HttpBaseChannel.h | 6 ++ netwerk/protocol/http/nsHttpChannel.cpp | 64 ++++++++----- netwerk/test/mochitests/mochitest.ini | 1 + netwerk/test/mochitests/redirect_to.sjs | 4 + .../test/mochitests/test_origin_header.html | 95 +++++++++++++++---- .../meta/fetch/origin/assorted.window.js.ini | 6 -- 11 files changed, 216 insertions(+), 99 deletions(-) create mode 100644 netwerk/test/mochitests/redirect_to.sjs diff --git a/browser/components/search/test/browser/mozsearch.sjs b/browser/components/search/test/browser/mozsearch.sjs index 584aabbfdd77..bde867c93e77 100644 --- a/browser/components/search/test/browser/mozsearch.sjs +++ b/browser/components/search/test/browser/mozsearch.sjs @@ -3,7 +3,7 @@ function handleRequest(req, resp) { resp.setHeader("Content-Type", "text/html", false); - if (req.hasHeader("Origin")) { + if (req.hasHeader("Origin") && req.getHeader("Origin") != "null") { resp.write("error"); return; } diff --git a/dom/fetch/FetchDriver.cpp b/dom/fetch/FetchDriver.cpp index 687ca29ee0f8..17d5193accef 100644 --- a/dom/fetch/FetchDriver.cpp +++ b/dom/fetch/FetchDriver.cpp @@ -1565,18 +1565,6 @@ void FetchDriver::SetRequestHeaders(nsIHttpChannel* aChannel, MOZ_ASSERT(NS_SUCCEEDED(rv)); } } - - nsAutoCString method; - mRequest->GetMethod(method); - if (!method.EqualsLiteral("GET") && !method.EqualsLiteral("HEAD")) { - nsAutoString origin; - if (NS_SUCCEEDED(nsContentUtils::GetUTFOrigin(mPrincipal, origin))) { - DebugOnly rv = aChannel->SetRequestHeader( - nsDependentCString(net::nsHttp::Origin), - NS_ConvertUTF16toUTF8(origin), false /* merge */); - MOZ_ASSERT(NS_SUCCEEDED(rv)); - } - } } void FetchDriver::RunAbortAlgorithm() { diff --git a/dom/security/ReferrerInfo.cpp b/dom/security/ReferrerInfo.cpp index f01b059f196d..0fc9a8b95a91 100644 --- a/dom/security/ReferrerInfo.cpp +++ b/dom/security/ReferrerInfo.cpp @@ -52,6 +52,42 @@ NS_IMPL_ISUPPORTS_CI(ReferrerInfo, nsIReferrerInfo, nsISerializable) #define MIN_CROSS_ORIGIN_SENDING_POLICY 0 #define MIN_TRIMMING_POLICY 0 +/* + * Default referrer policy to use + */ +enum DefaultReferrerPolicy : uint32_t { + eDefaultPolicyNoReferrer = 0, + eDefaultPolicySameOrgin = 1, + eDefaultPolicyStrictWhenXorigin = 2, + eDefaultPolicyNoReferrerWhenDownGrade = 3, +}; + +static uint32_t GetDefaultFirstPartyReferrerPolicyPref(bool privateBrowsing) { + return privateBrowsing + ? StaticPrefs::network_http_referer_defaultPolicy_pbmode() + : StaticPrefs::network_http_referer_defaultPolicy(); +} + +static uint32_t GetDefaultThirdPartyReferrerPolicyPref(bool privateBrowsing) { + return privateBrowsing + ? StaticPrefs::network_http_referer_defaultPolicy_trackers_pbmode() + : StaticPrefs::network_http_referer_defaultPolicy_trackers(); +} + +static ReferrerPolicy DefaultReferrerPolicyToReferrerPolicy( + uint32_t defaultToUse) { + switch (defaultToUse) { + case DefaultReferrerPolicy::eDefaultPolicyNoReferrer: + return ReferrerPolicy::No_referrer; + case DefaultReferrerPolicy::eDefaultPolicySameOrgin: + return ReferrerPolicy::Same_origin; + case DefaultReferrerPolicy::eDefaultPolicyStrictWhenXorigin: + return ReferrerPolicy::Strict_origin_when_cross_origin; + } + + return ReferrerPolicy::No_referrer_when_downgrade; +} + struct LegacyReferrerPolicyTokenMap { const char* mToken; ReferrerPolicy mPolicy; @@ -220,32 +256,10 @@ ReferrerPolicy ReferrerInfo::GetDefaultReferrerPolicy(nsIHttpChannel* aChannel, } } - uint32_t defaultToUse; - if (thirdPartyTrackerIsolated) { - if (privateBrowsing) { - defaultToUse = - StaticPrefs::network_http_referer_defaultPolicy_trackers_pbmode(); - } else { - defaultToUse = StaticPrefs::network_http_referer_defaultPolicy_trackers(); - } - } else { - if (privateBrowsing) { - defaultToUse = StaticPrefs::network_http_referer_defaultPolicy_pbmode(); - } else { - defaultToUse = StaticPrefs::network_http_referer_defaultPolicy(); - } - } - - switch (defaultToUse) { - case DefaultReferrerPolicy::eDefaultPolicyNoReferrer: - return ReferrerPolicy::No_referrer; - case DefaultReferrerPolicy::eDefaultPolicySameOrgin: - return ReferrerPolicy::Same_origin; - case DefaultReferrerPolicy::eDefaultPolicyStrictWhenXorigin: - return ReferrerPolicy::Strict_origin_when_cross_origin; - } - - return ReferrerPolicy::No_referrer_when_downgrade; + return DefaultReferrerPolicyToReferrerPolicy( + thirdPartyTrackerIsolated + ? GetDefaultThirdPartyReferrerPolicyPref(privateBrowsing) + : GetDefaultFirstPartyReferrerPolicyPref(privateBrowsing)); } /* static */ diff --git a/dom/security/ReferrerInfo.h b/dom/security/ReferrerInfo.h index a298388a4764..58df0cf45870 100644 --- a/dom/security/ReferrerInfo.h +++ b/dom/security/ReferrerInfo.h @@ -219,7 +219,16 @@ class ReferrerInfo : public nsIReferrerInfo { */ static ReferrerPolicyEnum GetDefaultReferrerPolicy( nsIHttpChannel* aChannel = nullptr, nsIURI* aURI = nullptr, - bool privateBrowsing = false); + bool aPrivateBrowsing = false); + + /** + * Return default referrer policy for third party which is controlled by user + * prefs: + * network.http.referer.defaultPolicy.trackers for regular mode + * network.http.referer.defaultPolicy.trackers.pbmode for private mode + */ + static ReferrerPolicyEnum GetDefaultThirdPartyReferrerPolicy( + bool aPrivateBrowsing = false); /* * Helper function to parse ReferrerPolicy from meta tag referrer content. @@ -275,16 +284,6 @@ class ReferrerInfo : public nsIReferrerInfo { ReferrerInfo(const ReferrerInfo& rhs); - /* - * Default referrer policy to use - */ - enum DefaultReferrerPolicy : uint32_t { - eDefaultPolicyNoReferrer = 0, - eDefaultPolicySameOrgin = 1, - eDefaultPolicyStrictWhenXorigin = 2, - eDefaultPolicyNoReferrerWhenDownGrade = 3, - }; - /* * Trimming policy when compute referrer, indicate how much information in the * referrer will be sent. Order matters here. diff --git a/netwerk/protocol/http/HttpBaseChannel.cpp b/netwerk/protocol/http/HttpBaseChannel.cpp index 4e8880c8e067..1394e9e654a3 100644 --- a/netwerk/protocol/http/HttpBaseChannel.cpp +++ b/netwerk/protocol/http/HttpBaseChannel.cpp @@ -223,6 +223,7 @@ HttpBaseChannel::HttpBaseChannel() mAsyncOpenWaitingForStreamLength(false), mUpgradableToSecure(true), mHasNonEmptySandboxingFlag(false), + mTaintedOriginFlag(false), mTlsFlags(0), mSuspendCount(0), mInitialRwin(0), @@ -4297,6 +4298,9 @@ nsresult HttpBaseChannel::SetupReplacementChannel(nsIURI* newURI, CallQueryInterface(newChannel, realChannel.StartAssignment()); if (realChannel) { realChannel->SetTopWindowURI(mTopWindowURI); + + realChannel->mTaintedOriginFlag = + ShouldTaintReplacementChannelOrigin(newURI); } // update the DocumentURI indicator since we are being redirected. @@ -4379,6 +4383,40 @@ nsresult HttpBaseChannel::SetupReplacementChannel(nsIURI* newURI, return NS_OK; } +bool HttpBaseChannel::ShouldTaintReplacementChannelOrigin(nsIURI* aNewURI) { + if (mTaintedOriginFlag) { + return true; + } + + nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager(); + if (!ssm) { + return true; + } + bool isPrivateWin = mLoadInfo->GetOriginAttributes().mPrivateBrowsingId > 0; + nsresult rv = ssm->CheckSameOriginURI(aNewURI, mURI, false, isPrivateWin); + if (NS_SUCCEEDED(rv)) { + return false; + } + // If aNewURI <-> mURI are not same-origin we need to taint unless + // mURI <-> mOriginalURI/LoadingPrincipal are same origin. + + if (mLoadInfo->GetLoadingPrincipal()) { + bool sameOrigin = false; + rv = mLoadInfo->GetLoadingPrincipal()->IsSameOrigin(mURI, isPrivateWin, + &sameOrigin); + if (NS_FAILED(rv)) { + return true; + } + return !sameOrigin; + } + if (!mOriginalURI) { + return true; + } + + rv = ssm->CheckSameOriginURI(mOriginalURI, mURI, false, isPrivateWin); + return NS_FAILED(rv); +} + // Redirect Tracking bool HttpBaseChannel::SameOriginWithOriginalUri(nsIURI* aURI) { nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager(); diff --git a/netwerk/protocol/http/HttpBaseChannel.h b/netwerk/protocol/http/HttpBaseChannel.h index a26ad5a9e530..0d3740b966d1 100644 --- a/netwerk/protocol/http/HttpBaseChannel.h +++ b/netwerk/protocol/http/HttpBaseChannel.h @@ -635,6 +635,9 @@ class HttpBaseChannel : public nsHashPropertyBag, nsCOMPtr mCurrentThread; private: + // WHATWG Fetch Standard 4.4. HTTP-redirect fetch, step 10 + bool ShouldTaintReplacementChannelOrigin(nsIURI* aNewURI); + // Proxy release all members above on main thread. void ReleaseMainThreadOnlyReferences(); @@ -824,6 +827,9 @@ class HttpBaseChannel : public nsHashPropertyBag, // True if the docshell's sandboxing flag set is not empty. uint32_t mHasNonEmptySandboxingFlag : 1; + // Tainted origin flag of a request, specified by WHATWG Fetch Standard 2.2.5. + uint32_t mTaintedOriginFlag : 1; + // An opaque flags for non-standard behavior of the TLS system. // It is unlikely this will need to be set outside of telemetry studies // relating to the TLS implementation. diff --git a/netwerk/protocol/http/nsHttpChannel.cpp b/netwerk/protocol/http/nsHttpChannel.cpp index 36116ae53054..fc9a8c2cf7c4 100644 --- a/netwerk/protocol/http/nsHttpChannel.cpp +++ b/netwerk/protocol/http/nsHttpChannel.cpp @@ -9371,16 +9371,34 @@ void nsHttpChannel::SetOriginHeader() { if (mRequestHead.IsGet() || mRequestHead.IsHead()) { return; } + if (mLoadInfo->TriggeringPrincipal()->IsSystemPrincipal()) { + // Do not set origin header for system principal contexts: + return; + } nsresult rv; nsAutoCString existingHeader; Unused << mRequestHead.GetHeader(nsHttp::Origin, existingHeader); if (!existingHeader.IsEmpty()) { LOG(("nsHttpChannel::SetOriginHeader Origin header already present")); - nsCOMPtr uri; - rv = NS_NewURI(getter_AddRefs(uri), existingHeader); - if (NS_SUCCEEDED(rv) && - ReferrerInfo::ShouldSetNullOriginHeader(this, uri)) { + // In case we already have an Origin header, check with referrerInfo + // if we should "null" it. + Unused << mRequestHead.GetHeader(nsHttp::Origin, existingHeader); + auto const shouldNullifyOriginHeader = + [&existingHeader](nsHttpChannel* self) { + if (self->mTaintedOriginFlag) { + return true; + } + + nsCOMPtr uri; + nsresult rv = NS_NewURI(getter_AddRefs(uri), existingHeader); + if (NS_FAILED(rv)) { + return false; + } + return ReferrerInfo::ShouldSetNullOriginHeader(self, uri); + }; + + if (shouldNullifyOriginHeader(this)) { LOG(("nsHttpChannel::SetOriginHeader null Origin by Referrer-Policy")); rv = mRequestHead.SetHeader(nsHttp::Origin, "null"_ns, false /* merge */); MOZ_ASSERT(NS_SUCCEEDED(rv)); @@ -9388,35 +9406,32 @@ void nsHttpChannel::SetOriginHeader() { return; } - if (StaticPrefs::network_http_sendOriginHeader() == 0) { - // Origin header suppressed by user setting - return; - } - nsCOMPtr referrer; auto* basePrin = BasePrincipal::Cast(mLoadInfo->TriggeringPrincipal()); - basePrin->GetURI(getter_AddRefs(referrer)); - if (!referrer || !dom::ReferrerInfo::IsReferrerSchemeAllowed(referrer)) { + rv = basePrin->GetURI(getter_AddRefs(referrer)); + if (NS_FAILED(rv)) { return; } nsAutoCString origin("null"); - nsContentUtils::GetASCIIOrigin(referrer, origin); - // Restrict Origin to same-origin loads if requested by user - if (StaticPrefs::network_http_sendOriginHeader() == 1) { - nsAutoCString currentOrigin; - nsContentUtils::GetASCIIOrigin(mURI, currentOrigin); - if (!origin.EqualsIgnoreCase(currentOrigin.get())) { - // Origin header suppressed by user setting - return; + if (StaticPrefs::network_http_sendOriginHeader() != 0 && referrer && + ReferrerInfo::IsReferrerSchemeAllowed(referrer) && + !ReferrerInfo::ShouldSetNullOriginHeader(this, referrer) && + !mTaintedOriginFlag) { + nsContentUtils::GetASCIIOrigin(referrer, origin); + + // Restrict Origin to same-origin loads if requested by user + if (StaticPrefs::network_http_sendOriginHeader() == 1) { + nsAutoCString currentOrigin; + nsContentUtils::GetASCIIOrigin(mURI, currentOrigin); + if (!origin.EqualsIgnoreCase(currentOrigin.get())) { + // Origin header suppressed by user setting + origin.AssignLiteral("null"); + } } } - if (ReferrerInfo::ShouldSetNullOriginHeader(this, referrer)) { - origin.AssignLiteral("null"); - } - rv = mRequestHead.SetHeader(nsHttp::Origin, origin, false /* merge */); MOZ_ASSERT(NS_SUCCEEDED(rv)); } @@ -10033,7 +10048,8 @@ void nsHttpChannel::ReEvaluateReferrerAfterTrackingStatusIsKnown() { ReferrerInfo::GetDefaultReferrerPolicy(nullptr, nullptr, isPrivate)) { nsCOMPtr newReferrerInfo = - referrerInfo->CloneWithNewPolicy(ReferrerPolicy::_empty); + referrerInfo->CloneWithNewPolicy( + ReferrerInfo::GetDefaultReferrerPolicy(this, mURI, isPrivate)); // The arguments passed to SetReferrerInfoInternal here should mirror // the arguments passed in // HttpChannelChild::RecvOverrideReferrerInfoDuringBeginConnect(). diff --git a/netwerk/test/mochitests/mochitest.ini b/netwerk/test/mochitests/mochitest.ini index 7fdaf884a2c9..77a8e8501fb1 100644 --- a/netwerk/test/mochitests/mochitest.ini +++ b/netwerk/test/mochitests/mochitest.ini @@ -16,6 +16,7 @@ support-files = redirect_idn.html empty.html redirect.sjs + redirect_to.sjs origin_header.sjs origin_header_form_post.html origin_header_form_post_xorigin.html diff --git a/netwerk/test/mochitests/redirect_to.sjs b/netwerk/test/mochitests/redirect_to.sjs new file mode 100644 index 000000000000..f090c70849fa --- /dev/null +++ b/netwerk/test/mochitests/redirect_to.sjs @@ -0,0 +1,4 @@ +function handleRequest(request, response) { + response.setStatusLine(request.httpVersion, 308, "Permanent Redirect"); + response.setHeader("Location", request.queryString); +} diff --git a/netwerk/test/mochitests/test_origin_header.html b/netwerk/test/mochitests/test_origin_header.html index c6bbdfd234af..5c878f1ed16a 100644 --- a/netwerk/test/mochitests/test_origin_header.html +++ b/netwerk/test/mochitests/test_origin_header.html @@ -23,15 +23,18 @@ let testsToRun = [ ["network.http.sendOriginHeader", 0], ], results: { - framePost: EMPTY_ORIGIN, - framePostXOrigin: EMPTY_ORIGIN, + framePost: "Origin: null", + framePostXOrigin: "Origin: null", frameGet: EMPTY_ORIGIN, - framePostNonSandboxed: EMPTY_ORIGIN, - framePostNonSandboxedXOrigin: EMPTY_ORIGIN, - framePostSandboxed: EMPTY_ORIGIN, - framePostSrcDoc: EMPTY_ORIGIN, - framePostSrcDocXOrigin: EMPTY_ORIGIN, - framePostDataURI: EMPTY_ORIGIN, + framePostNonSandboxed: "Origin: null", + framePostNonSandboxedXOrigin: "Origin: null", + framePostSandboxed: "Origin: null", + framePostSrcDoc: "Origin: null", + framePostSrcDocXOrigin: "Origin: null", + framePostDataURI: "Origin: null", + framePostSameOriginToXOrigin: "Origin: null", + framePostXOriginToSameOrigin: "Origin: null", + framePostXOriginToXOrigin: "Origin: null", }, }, { @@ -41,14 +44,17 @@ let testsToRun = [ ], results: { framePost: "Origin: http://mochi.test:8888", - framePostXOrigin: EMPTY_ORIGIN, + framePostXOrigin: "Origin: null", frameGet: EMPTY_ORIGIN, framePostNonSandboxed: "Origin: http://mochi.test:8888", - framePostNonSandboxedXOrigin: EMPTY_ORIGIN, - framePostSandboxed: EMPTY_ORIGIN, + framePostNonSandboxedXOrigin: "Origin: null", + framePostSandboxed: "Origin: null", framePostSrcDoc: "Origin: http://mochi.test:8888", - framePostSrcDocXOrigin: EMPTY_ORIGIN, - framePostDataURI: EMPTY_ORIGIN, + framePostSrcDocXOrigin: "Origin: null", + framePostDataURI: "Origin: null", + framePostSameOriginToXOrigin: "Origin: null", + framePostXOriginToSameOrigin: "Origin: null", + framePostXOriginToXOrigin: "Origin: null", }, }, { @@ -62,10 +68,13 @@ let testsToRun = [ frameGet: EMPTY_ORIGIN, framePostNonSandboxed: "Origin: http://mochi.test:8888", framePostNonSandboxedXOrigin: "Origin: http://mochi.test:8888", - framePostSandboxed: EMPTY_ORIGIN, + framePostSandboxed: "Origin: null", framePostSrcDoc: "Origin: http://mochi.test:8888", framePostSrcDocXOrigin: "Origin: http://mochi.test:8888", - framePostDataURI: EMPTY_ORIGIN, + framePostDataURI: "Origin: null", + framePostSameOriginToXOrigin: "Origin: http://mochi.test:8888", + framePostXOriginToSameOrigin: "Origin: null", + framePostXOriginToXOrigin: "Origin: http://mochi.test:8888", }, }, { @@ -79,10 +88,13 @@ let testsToRun = [ frameGet: EMPTY_ORIGIN, framePostNonSandboxed: "Origin: http://mochi.test:8888", framePostNonSandboxedXOrigin: "Origin: http://mochi.test:8888", - framePostSandboxed: EMPTY_ORIGIN, + framePostSandboxed: "Origin: null", framePostSrcDoc: "Origin: http://mochi.test:8888", framePostSrcDocXOrigin: "Origin: http://mochi.test:8888", - framePostDataURI: EMPTY_ORIGIN, + framePostDataURI: "Origin: null", + framePostSameOriginToXOrigin: "Origin: http://mochi.test:8888", + framePostXOriginToSameOrigin: "Origin: null", + framePostXOriginToXOrigin: "Origin: http://mochi.test:8888", }, }, { @@ -97,10 +109,13 @@ let testsToRun = [ frameGet: EMPTY_ORIGIN, framePostNonSandboxed: "Origin: null", framePostNonSandboxedXOrigin: "Origin: null", - framePostSandboxed: EMPTY_ORIGIN, + framePostSandboxed: "Origin: null", framePostSrcDoc: "Origin: null", framePostSrcDocXOrigin: "Origin: null", - framePostDataURI: EMPTY_ORIGIN, + framePostDataURI: "Origin: null", + framePostSameOriginToXOrigin: "Origin: null", + framePostXOriginToSameOrigin: "Origin: null", + framePostXOriginToXOrigin: "Origin: null", }, }, ]; @@ -151,6 +166,21 @@ let checksToRun = [ frameID: "framePostDataURI", dataURI: "origin_header_form_post.html", }, + { + name: "same-origin POST redirected to cross-origin", + frameID: "framePostSameOriginToXOrigin", + formID: "formPostSameOriginToXOrigin", + }, + { + name: "cross-origin POST redirected to same-origin", + frameID: "framePostXOriginToSameOrigin", + formID: "formPostXOriginToSameOrigin", + }, + { + name: "cross-origin POST redirected to cross-origin", + frameID: "framePostXOriginToXOrigin", + formID: "formPostXOriginToXOrigin", + }, ]; function frameLoaded(test, check) @@ -306,6 +336,33 @@ addLoadEvent(runTests); + + +
+ +
+ + + +
+ +
+ + + +
+ +
+ diff --git a/testing/web-platform/meta/fetch/origin/assorted.window.js.ini b/testing/web-platform/meta/fetch/origin/assorted.window.js.ini index 73d1573acce9..c502b8b3963a 100644 --- a/testing/web-platform/meta/fetch/origin/assorted.window.js.ini +++ b/testing/web-platform/meta/fetch/origin/assorted.window.js.ini @@ -1,10 +1,4 @@ [assorted.window.html] - [Origin header and 308 redirect] - expected: FAIL - - [Origin header and POST navigation] - expected: FAIL - [Origin header and POST same-origin fetch cors mode with Referrer-Policy no-referrer] expected: FAIL