Bug 1802086 - remove auth header from redirected cross-origin requests. r=necko-reviewers,smaug,valentin

The change also updates WPT based on the discussions here https://github.com/web-platform-tests/wpt/pull/37145/files#r1042166622

Differential Revision: https://phabricator.services.mozilla.com/D163904
This commit is contained in:
sunil mayya 2023-01-17 16:13:51 +00:00
parent f5765da017
commit cb305f0962
10 changed files with 65 additions and 70 deletions

View File

@ -8,6 +8,7 @@
#include "mozilla/DebugOnly.h"
#include "mozilla/dom/FetchDriver.h"
#include "mozilla/dom/ReferrerInfo.h"
#include "nsIAsyncVerifyRedirectCallback.h"
#include "mozilla/dom/Document.h"
#include "nsICookieJarSettings.h"
@ -713,7 +714,7 @@ nsresult FetchDriver::HttpFetch(
NS_ENSURE_SUCCESS(rv, rv);
// Set the same headers.
SetRequestHeaders(httpChan, false);
SetRequestHeaders(httpChan, false, false);
// Step 5 of https://fetch.spec.whatwg.org/#main-fetch
// If request's referrer policy is the empty string and request's client is
@ -1529,7 +1530,14 @@ FetchDriver::AsyncOnChannelRedirect(nsIChannel* aOldChannel,
Unused << oldHttpChannel->ShouldStripRequestBodyHeader(method,
&rewriteToGET);
SetRequestHeaders(newHttpChannel, rewriteToGET);
// we need to strip Authentication headers for cross-origin requests
// Ref: https://fetch.spec.whatwg.org/#http-redirect-fetch
bool skipAuthHeader = false;
if (StaticPrefs::network_fetch_redirect_stripAuthHeader()) {
skipAuthHeader = ReferrerInfo::IsCrossOriginRequest(oldHttpChannel);
}
SetRequestHeaders(newHttpChannel, rewriteToGET, skipAuthHeader);
}
// "HTTP-redirect fetch": step 14 "Append locationURL to request's URL list."
@ -1637,7 +1645,8 @@ PerformanceTimingData* FetchDriver::GetPerformanceTimingData(
}
void FetchDriver::SetRequestHeaders(nsIHttpChannel* aChannel,
bool aStripRequestBodyHeader) const {
bool aStripRequestBodyHeader,
bool aStripAuthHeader) const {
MOZ_ASSERT(aChannel);
// nsIHttpChannel has a set of pre-configured headers (Accept,
@ -1658,6 +1667,11 @@ void FetchDriver::SetRequestHeaders(nsIHttpChannel* aChannel,
continue;
}
if (aStripAuthHeader &&
headers[i].mName.LowerCaseEqualsASCII("authorization")) {
continue;
}
bool alreadySet = headersSet.Contains(headers[i].mName);
if (!alreadySet) {
headersSet.AppendElement(headers[i].mName);

View File

@ -207,8 +207,8 @@ class FetchDriver final : public nsIStreamListener,
// response.
void FailWithNetworkError(nsresult rv);
void SetRequestHeaders(nsIHttpChannel* aChannel,
bool aStripRequestBodyHeader) const;
void SetRequestHeaders(nsIHttpChannel* aChannel, bool aStripRequestBodyHeader,
bool aStripAuthHeader) const;
void FinishOnStopRequest(AlternativeDataStreamListener* aAltDataListener);
};

View File

@ -2567,7 +2567,7 @@ nsresult XMLHttpRequestMainThread::InitiateFetch(
mAuthorRequestHeaders.Set("accept", "*/*"_ns);
}
mAuthorRequestHeaders.ApplyToChannel(httpChannel, false);
mAuthorRequestHeaders.ApplyToChannel(httpChannel, false, false);
if (!IsSystemXHR()) {
nsCOMPtr<nsPIDOMWindowInner> owner = GetOwner();
@ -3374,9 +3374,17 @@ nsresult XMLHttpRequestMainThread::OnRedirectVerifyCallback(nsresult result) {
nsCOMPtr<nsIHttpChannel> newHttpChannel(do_QueryInterface(mChannel));
if (newHttpChannel) {
// we need to strip Authentication headers for cross-origin requests
// Ref: https://fetch.spec.whatwg.org/#http-redirect-fetch
bool skipAuthHeader = false;
if (StaticPrefs::network_fetch_redirect_stripAuthHeader()) {
skipAuthHeader = ReferrerInfo::IsCrossOriginRequest(oldHttpChannel);
}
// Ensure all original headers are duplicated for the new channel (bug
// #553888)
mAuthorRequestHeaders.ApplyToChannel(newHttpChannel, rewriteToGET);
mAuthorRequestHeaders.ApplyToChannel(newHttpChannel, rewriteToGET,
skipAuthHeader);
}
} else {
mErrorLoad = ErrorType::eRedirect;
@ -4069,7 +4077,8 @@ void RequestHeaders::MergeOrSet(const nsACString& aName,
void RequestHeaders::Clear() { mHeaders.Clear(); }
void RequestHeaders::ApplyToChannel(nsIHttpChannel* aChannel,
bool aStripRequestBodyHeader) const {
bool aStripRequestBodyHeader,
bool aStripAuthHeader) const {
for (const RequestHeader& header : mHeaders) {
if (aStripRequestBodyHeader &&
(header.mName.LowerCaseEqualsASCII("content-type") ||
@ -4078,6 +4087,12 @@ void RequestHeaders::ApplyToChannel(nsIHttpChannel* aChannel,
header.mName.LowerCaseEqualsASCII("content-location"))) {
continue;
}
if (aStripAuthHeader &&
header.mName.LowerCaseEqualsASCII("authorization")) {
continue;
}
// Update referrerInfo to override referrer header in system privileged.
if (header.mName.LowerCaseEqualsASCII("referer")) {
DebugOnly<nsresult> rv = aChannel->SetNewReferrerInfo(

View File

@ -168,8 +168,8 @@ class RequestHeaders {
void MergeOrSet(const char* aName, const nsACString& aValue);
void MergeOrSet(const nsACString& aName, const nsACString& aValue);
void Clear();
void ApplyToChannel(nsIHttpChannel* aChannel,
bool aStripRequestBodyHeader) const;
void ApplyToChannel(nsIHttpChannel* aChannel, bool aStripRequestBodyHeader,
bool aStripAuthHeader) const;
void GetCORSUnsafeHeaders(nsTArray<nsCString>& aArray) const;
};

View File

@ -11005,6 +11005,20 @@
value: true
mirror: always
# Whether to strip auth headers for redirected fetch/xhr channels
# https://fetch.spec.whatwg.org/#http-redirect-fetch
- name: network.fetch.redirect.stripAuthHeader
type: RelaxedAtomicBool
value: true
mirror: always
# Whether to strip auth headers for redirected http channels
# https://fetch.spec.whatwg.org/#http-redirect-fetch
- name: network.http.redirect.stripAuthHeader
type: RelaxedAtomicBool
value: true
mirror: always
# Prefs allowing granular control of referers.
# 0=don't send any, 1=send only on clicks, 2=send on image requests as well
- name: network.http.sendRefererHeader

View File

@ -4900,6 +4900,16 @@ nsresult HttpBaseChannel::SetupReplacementChannel(nsIURI* newURI,
MOZ_ASSERT(NS_SUCCEEDED(rv));
}
// we need to strip Authentication headers for cross-origin requests
// Ref: https://fetch.spec.whatwg.org/#http-redirect-fetch
nsAutoCString authHeader;
if (StaticPrefs::network_http_redirect_stripAuthHeader() &&
NS_SUCCEEDED(
httpChannel->GetRequestHeader("Authorization"_ns, authHeader))) {
rv = httpChannel->SetRequestHeader("Authorization"_ns, ""_ns, false);
MOZ_ASSERT(NS_SUCCEEDED(rv));
}
return NS_OK;
}

View File

@ -1,26 +0,0 @@
[authentication-redirection.any.serviceworker.html]
expected:
if (os == "android") and fission: [OK, TIMEOUT]
[getAuthorizationHeaderValue - cross origin redirection]
expected: FAIL
[authentication-redirection.any.sharedworker.html]
expected:
if (os == "android") and fission: [OK, TIMEOUT]
[getAuthorizationHeaderValue - cross origin redirection]
expected: FAIL
[authentication-redirection.any.html]
expected:
if (os == "android") and fission: [OK, TIMEOUT]
[getAuthorizationHeaderValue - cross origin redirection]
expected: FAIL
[authentication-redirection.any.worker.html]
expected:
if (os == "android") and fission: [OK, TIMEOUT]
[getAuthorizationHeaderValue - cross origin redirection]
expected: FAIL

View File

@ -1,32 +0,0 @@
[xhr-authorization-redirect.any.html]
expected:
if (os == "android") and fission: [OK, TIMEOUT]
[getAuthorizationHeaderValue - cross origin redirection]
expected: FAIL
[xhr-authorization-redirect.any.serviceworker.html]
expected:
if (os == "android") and fission: [OK, TIMEOUT]
[getAuthorizationHeaderValue - no redirection]
expected: FAIL
[getAuthorizationHeaderValue - same origin redirection]
expected: FAIL
[getAuthorizationHeaderValue - cross origin redirection]
expected: FAIL
[xhr-authorization-redirect.any.worker.html]
expected:
if (os == "android") and fission: [OK, TIMEOUT]
[getAuthorizationHeaderValue - cross origin redirection]
expected: FAIL
[xhr-authorization-redirect.any.sharedworker.html]
expected:
if (os == "android") and fission: [OK, TIMEOUT]
[getAuthorizationHeaderValue - cross origin redirection]
expected: FAIL

View File

@ -21,6 +21,6 @@ promise_test(async test => {
}, "getAuthorizationHeaderValue - same origin redirection");
promise_test(async (test) => {
const result = await getAuthorizationHeaderValue(get_host_info().HTTP_REMOTE_ORIGIN + "/fetch/api/resources/redirect.py?allow_headers=Authorization&location=" + encodeURIComponent(get_host_info().HTTP_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"));
assert_equals(result, "none");
}, "getAuthorizationHeaderValue - cross origin redirection");

View File

@ -1,4 +1,4 @@
// META: global=window,worker
// META: global=window,sharedworker,dedicatedworker
// META: script=/common/get-host-info.sub.js
const authorizationValue = "Basic " + btoa("user:pass");