Bug 1874132 - remove auth headers from preflight request for cross origin requests. r=necko-reviewers,valentin

Differential Revision: https://phabricator.services.mozilla.com/D204608
This commit is contained in:
sunil mayya 2024-03-18 08:48:10 +00:00
parent b3a7aa69b7
commit 1599e97480
10 changed files with 51 additions and 27 deletions

View file

@ -4973,7 +4973,7 @@ nsresult HttpBaseChannel::SetupReplacementChannel(nsIURI* newURI,
httpInternal->SetLastRedirectFlags(redirectFlags); httpInternal->SetLastRedirectFlags(redirectFlags);
if (LoadRequireCORSPreflight()) { if (LoadRequireCORSPreflight()) {
httpInternal->SetCorsPreflightParameters(mUnsafeHeaders, false); httpInternal->SetCorsPreflightParameters(mUnsafeHeaders, false, false);
} }
} }
@ -5847,17 +5847,20 @@ void HttpBaseChannel::EnsureBrowserId() {
void HttpBaseChannel::SetCorsPreflightParameters( void HttpBaseChannel::SetCorsPreflightParameters(
const nsTArray<nsCString>& aUnsafeHeaders, const nsTArray<nsCString>& aUnsafeHeaders,
bool aShouldStripRequestBodyHeader) { bool aShouldStripRequestBodyHeader, bool aShouldStripAuthHeader) {
MOZ_RELEASE_ASSERT(!LoadRequestObserversCalled()); MOZ_RELEASE_ASSERT(!LoadRequestObserversCalled());
StoreRequireCORSPreflight(true); StoreRequireCORSPreflight(true);
mUnsafeHeaders = aUnsafeHeaders.Clone(); mUnsafeHeaders = aUnsafeHeaders.Clone();
if (aShouldStripRequestBodyHeader) { if (aShouldStripRequestBodyHeader || aShouldStripAuthHeader) {
mUnsafeHeaders.RemoveElementsBy([&](const nsCString& aHeader) { mUnsafeHeaders.RemoveElementsBy([&](const nsCString& aHeader) {
return aHeader.LowerCaseEqualsASCII("content-type") || return (aShouldStripRequestBodyHeader &&
(aHeader.LowerCaseEqualsASCII("content-type") ||
aHeader.LowerCaseEqualsASCII("content-encoding") || aHeader.LowerCaseEqualsASCII("content-encoding") ||
aHeader.LowerCaseEqualsASCII("content-language") || aHeader.LowerCaseEqualsASCII("content-language") ||
aHeader.LowerCaseEqualsASCII("content-location"); aHeader.LowerCaseEqualsASCII("content-location"))) ||
(aShouldStripAuthHeader &&
aHeader.LowerCaseEqualsASCII("authorization"));
}); });
} }
} }

View file

@ -319,7 +319,7 @@ class HttpBaseChannel : public nsHashPropertyBag,
NS_IMETHOD GetProxyURI(nsIURI** proxyURI) override; NS_IMETHOD GetProxyURI(nsIURI** proxyURI) override;
virtual void SetCorsPreflightParameters( virtual void SetCorsPreflightParameters(
const nsTArray<nsCString>& unsafeHeaders, const nsTArray<nsCString>& unsafeHeaders,
bool aShouldStripRequestBodyHeader) override; bool aShouldStripRequestBodyHeader, bool aShouldStripAuthHeader) override;
virtual void SetAltDataForChild(bool aIsForChild) override; virtual void SetAltDataForChild(bool aIsForChild) override;
virtual void DisableAltDataCache() override { virtual void DisableAltDataCache() override {
StoreDisableAltDataCache(true); StoreDisableAltDataCache(true);

View file

@ -587,7 +587,7 @@ bool HttpChannelParent::DoAsyncOpen(
if (aCorsPreflightArgs.isSome()) { if (aCorsPreflightArgs.isSome()) {
const CorsPreflightArgs& args = aCorsPreflightArgs.ref(); const CorsPreflightArgs& args = aCorsPreflightArgs.ref();
httpChannel->SetCorsPreflightParameters(args.unsafeHeaders(), false); httpChannel->SetCorsPreflightParameters(args.unsafeHeaders(), false, false);
} }
nsCOMPtr<nsIInputStream> stream = DeserializeIPCStream(uploadStream); nsCOMPtr<nsIInputStream> stream = DeserializeIPCStream(uploadStream);
@ -896,7 +896,7 @@ mozilla::ipc::IPCResult HttpChannelParent::RecvRedirect2Verify(
MOZ_RELEASE_ASSERT(newInternalChannel); MOZ_RELEASE_ASSERT(newInternalChannel);
const CorsPreflightArgs& args = aCorsPreflightArgs.ref(); const CorsPreflightArgs& args = aCorsPreflightArgs.ref();
newInternalChannel->SetCorsPreflightParameters(args.unsafeHeaders(), newInternalChannel->SetCorsPreflightParameters(args.unsafeHeaders(),
false); false, false);
} }
if (aReferrerInfo) { if (aReferrerInfo) {

View file

@ -419,10 +419,10 @@ void ObliviousHttpChannel::SetAltDataForChild(bool aIsForChild) {
void ObliviousHttpChannel::SetCorsPreflightParameters( void ObliviousHttpChannel::SetCorsPreflightParameters(
nsTArray<nsTString<char>> const& aUnsafeHeaders, nsTArray<nsTString<char>> const& aUnsafeHeaders,
bool aShouldStripRequestBodyHeader) { bool aShouldStripRequestBodyHeader, bool aShouldStripAuthHeader) {
if (mInnerChannelInternal) { if (mInnerChannelInternal) {
mInnerChannelInternal->SetCorsPreflightParameters( mInnerChannelInternal->SetCorsPreflightParameters(
aUnsafeHeaders, aShouldStripRequestBodyHeader); aUnsafeHeaders, aShouldStripRequestBodyHeader, aShouldStripAuthHeader);
} }
} }

View file

@ -441,7 +441,8 @@ nsresult nsCORSListenerProxy::Init(nsIChannel* aChannel,
getter_AddRefs(mOuterNotificationCallbacks)); getter_AddRefs(mOuterNotificationCallbacks));
aChannel->SetNotificationCallbacks(this); aChannel->SetNotificationCallbacks(this);
nsresult rv = UpdateChannel(aChannel, aAllowDataURI, UpdateType::Default); nsresult rv =
UpdateChannel(aChannel, aAllowDataURI, UpdateType::Default, false);
if (NS_FAILED(rv)) { if (NS_FAILED(rv)) {
{ {
MutexAutoLock lock(mMutex); MutexAutoLock lock(mMutex);
@ -765,7 +766,7 @@ nsCORSListenerProxy::AsyncOnChannelRedirect(
// data URIs should have been blocked before we got to the internal // data URIs should have been blocked before we got to the internal
// redirect. // redirect.
rv = UpdateChannel(aNewChannel, DataURIHandling::Allow, rv = UpdateChannel(aNewChannel, DataURIHandling::Allow,
UpdateType::InternalOrHSTSRedirect); UpdateType::InternalOrHSTSRedirect, false);
if (NS_FAILED(rv)) { if (NS_FAILED(rv)) {
NS_WARNING( NS_WARNING(
"nsCORSListenerProxy::AsyncOnChannelRedirect: " "nsCORSListenerProxy::AsyncOnChannelRedirect: "
@ -835,6 +836,13 @@ nsCORSListenerProxy::AsyncOnChannelRedirect(
} }
bool rewriteToGET = false; bool rewriteToGET = false;
// We need to strip auth header from preflight request for
// cross-origin redirects.
// See Bug 1874132
bool stripAuthHeader =
StaticPrefs::network_fetch_redirect_stripAuthHeader() &&
NS_ShouldRemoveAuthHeaderOnRedirect(aOldChannel, aNewChannel, aFlags);
nsCOMPtr<nsIHttpChannel> oldHttpChannel = do_QueryInterface(aOldChannel); nsCOMPtr<nsIHttpChannel> oldHttpChannel = do_QueryInterface(aOldChannel);
if (oldHttpChannel) { if (oldHttpChannel) {
nsAutoCString method; nsAutoCString method;
@ -843,9 +851,10 @@ nsCORSListenerProxy::AsyncOnChannelRedirect(
&rewriteToGET); &rewriteToGET);
} }
rv = UpdateChannel(aNewChannel, DataURIHandling::Disallow, rv = UpdateChannel(
rewriteToGET ? UpdateType::StripRequestBodyHeader aNewChannel, DataURIHandling::Disallow,
: UpdateType::Default); rewriteToGET ? UpdateType::StripRequestBodyHeader : UpdateType::Default,
stripAuthHeader);
if (NS_FAILED(rv)) { if (NS_FAILED(rv)) {
NS_WARNING( NS_WARNING(
"nsCORSListenerProxy::AsyncOnChannelRedirect: " "nsCORSListenerProxy::AsyncOnChannelRedirect: "
@ -930,7 +939,10 @@ bool CheckInsecureUpgradePreventsCORS(nsIPrincipal* aRequestingPrincipal,
nsresult nsCORSListenerProxy::UpdateChannel(nsIChannel* aChannel, nsresult nsCORSListenerProxy::UpdateChannel(nsIChannel* aChannel,
DataURIHandling aAllowDataURI, DataURIHandling aAllowDataURI,
UpdateType aUpdateType) { UpdateType aUpdateType,
bool aStripAuthHeader) {
MOZ_ASSERT_IF(aUpdateType == UpdateType::InternalOrHSTSRedirect,
!aStripAuthHeader);
nsCOMPtr<nsIURI> uri, originalURI; nsCOMPtr<nsIURI> uri, originalURI;
nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(uri)); nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(uri));
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
@ -1020,7 +1032,7 @@ nsresult nsCORSListenerProxy::UpdateChannel(nsIChannel* aChannel,
// Check if we need to do a preflight, and if so set one up. This must be // Check if we need to do a preflight, and if so set one up. This must be
// called once we know that the request is going, or has gone, cross-origin. // called once we know that the request is going, or has gone, cross-origin.
rv = CheckPreflightNeeded(aChannel, aUpdateType); rv = CheckPreflightNeeded(aChannel, aUpdateType, aStripAuthHeader);
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
// It's a cross site load // It's a cross site load
@ -1087,7 +1099,8 @@ nsresult nsCORSListenerProxy::UpdateChannel(nsIChannel* aChannel,
} }
nsresult nsCORSListenerProxy::CheckPreflightNeeded(nsIChannel* aChannel, nsresult nsCORSListenerProxy::CheckPreflightNeeded(nsIChannel* aChannel,
UpdateType aUpdateType) { UpdateType aUpdateType,
bool aStripAuthHeader) {
// If this caller isn't using AsyncOpen, or if this *is* a preflight channel, // If this caller isn't using AsyncOpen, or if this *is* a preflight channel,
// then we shouldn't initiate preflight for this channel. // then we shouldn't initiate preflight for this channel.
nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo(); nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
@ -1154,7 +1167,7 @@ nsresult nsCORSListenerProxy::CheckPreflightNeeded(nsIChannel* aChannel,
internal->SetCorsPreflightParameters( internal->SetCorsPreflightParameters(
headers.IsEmpty() ? loadInfoHeaders : headers, headers.IsEmpty() ? loadInfoHeaders : headers,
aUpdateType == UpdateType::StripRequestBodyHeader); aUpdateType == UpdateType::StripRequestBodyHeader, aStripAuthHeader);
return NS_OK; return NS_OK;
} }

View file

@ -92,10 +92,12 @@ class nsCORSListenerProxy final : public nsIInterfaceRequestor,
[[nodiscard]] nsresult UpdateChannel(nsIChannel* aChannel, [[nodiscard]] nsresult UpdateChannel(nsIChannel* aChannel,
DataURIHandling aAllowDataURI, DataURIHandling aAllowDataURI,
UpdateType aUpdateType); UpdateType aUpdateType,
bool aStripAuthHeader);
[[nodiscard]] nsresult CheckRequestApproved(nsIRequest* aRequest); [[nodiscard]] nsresult CheckRequestApproved(nsIRequest* aRequest);
[[nodiscard]] nsresult CheckPreflightNeeded(nsIChannel* aChannel, [[nodiscard]] nsresult CheckPreflightNeeded(nsIChannel* aChannel,
UpdateType aUpdateType); UpdateType aUpdateType,
bool aStripAuthHeader);
nsCOMPtr<nsIStreamListener> mOuterListener; nsCOMPtr<nsIStreamListener> mOuterListener;
// The principal that originally kicked off the request // The principal that originally kicked off the request

View file

@ -381,7 +381,8 @@ interface nsIHttpChannelInternal : nsISupports
*/ */
[noscript, notxpcom, nostdcall] [noscript, notxpcom, nostdcall]
void setCorsPreflightParameters(in CStringArrayRef unsafeHeaders, void setCorsPreflightParameters(in CStringArrayRef unsafeHeaders,
in boolean shouldStripRequestBodyHeader); in boolean shouldStripRequestBodyHeader,
in boolean shouldStripAuthHeader);
[noscript, notxpcom, nostdcall] [noscript, notxpcom, nostdcall]
void setAltDataForChild(in boolean aIsForChild); void setAltDataForChild(in boolean aIsForChild);

View file

@ -1048,9 +1048,9 @@ NS_IMETHODIMP nsViewSourceChannel::GetDocumentCharacterSet(
// Have to manually forward SetCorsPreflightParameters since it's [notxpcom] // Have to manually forward SetCorsPreflightParameters since it's [notxpcom]
void nsViewSourceChannel::SetCorsPreflightParameters( void nsViewSourceChannel::SetCorsPreflightParameters(
const nsTArray<nsCString>& aUnsafeHeaders, const nsTArray<nsCString>& aUnsafeHeaders,
bool aShouldStripRequestBodyHeader) { bool aShouldStripRequestBodyHeader, bool aShouldStripAuthHeader) {
mHttpChannelInternal->SetCorsPreflightParameters( mHttpChannelInternal->SetCorsPreflightParameters(
aUnsafeHeaders, aShouldStripRequestBodyHeader); aUnsafeHeaders, aShouldStripRequestBodyHeader, aShouldStripAuthHeader);
} }
void nsViewSourceChannel::SetAltDataForChild(bool aIsForChild) { void nsViewSourceChannel::SetAltDataForChild(bool aIsForChild) {

View file

@ -24,6 +24,6 @@ promise_test(async test => {
}, "getAuthorizationHeaderValue - same origin redirection"); }, "getAuthorizationHeaderValue - same origin redirection");
promise_test(async (test) => { promise_test(async (test) => {
const result = await getAuthorizationHeaderValue(get_host_info().HTTPS_REMOTE_ORIGIN + "/fetch/api/resources/redirect.py?allow_headers=Authorization&location=" + encodeURIComponent(get_host_info().HTTPS_ORIGIN + "/fetch/api/resources/dump-authorization-header.py")); const result = await getAuthorizationHeaderValue(get_host_info().HTTPS_REMOTE_ORIGIN + "/fetch/api/resources/redirect.py?allow_headers=Authorization&location=" + encodeURIComponent(get_host_info().HTTPS_ORIGIN + "/fetch/api/resources/dump-authorization-header.py?strip_auth_header=true"));
assert_equals(result, "none"); assert_equals(result, "none");
}, "getAuthorizationHeaderValue - cross origin redirection"); }, "getAuthorizationHeaderValue - cross origin redirection");

View file

@ -2,6 +2,11 @@ def main(request, response):
headers = [(b"Content-Type", "text/html"), headers = [(b"Content-Type", "text/html"),
(b"Cache-Control", b"no-cache")] (b"Cache-Control", b"no-cache")]
if (request.GET.first(b"strip_auth_header", False) and request.method == "OPTIONS" and
b"authorization" in request.headers.get(b"Access-Control-Request-Headers", b"").lower()):
# Auth header should not be sent for preflight after cross-origin redirect.
return 500, headers, "fail"
if b"Origin" in request.headers: if b"Origin" in request.headers:
headers.append((b"Access-Control-Allow-Origin", request.headers.get(b"Origin", b""))) headers.append((b"Access-Control-Allow-Origin", request.headers.get(b"Origin", b"")))
headers.append((b"Access-Control-Allow-Credentials", b"true")) headers.append((b"Access-Control-Allow-Credentials", b"true"))