forked from mirrors/gecko-dev
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:
parent
b3a7aa69b7
commit
1599e97480
10 changed files with 51 additions and 27 deletions
|
|
@ -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"));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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) {
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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) {
|
||||||
|
|
|
||||||
|
|
@ -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");
|
||||||
|
|
|
||||||
|
|
@ -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"))
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue