Bug 1817980 - replace ReferrerInfo::IsCrossOriginRequest with nsScriptSecurityManager::CheckSameOriginURI for determining cross-origin redirects. r=necko-reviewers,valentin

Since Bug 1802086, we strip authentication headers when redirected to a cross-origin page. However, the api   ReferrerInfo::IsCrossOriginRequest  used for determining whether a request is cross-origin cannot be used as it compares the triggering principal's uri with the redirected channel's uri. This comparison might sometimes yield to false positives.
For e.g consider the following scenario:
1. Load `https://example.org/` and send the following fetch request from browser console
```
fetch("https://test.com/some_location", {
  "headers": {
    "Authorization": "Token foo"
  }
});
```
2. Server responds with  a redirect to https://test.com/another_location

In the above scenario, the api ReferrerInfo::IsCrossOriginRequest  will yield the above request as cross origin since the triggering principal uri here is example.com. Hence, this will be treated as cross-origin redirect resulting in removal of auth headers.

Thus ReferrerInfo::IsCrossOriginRequest  has been replaced with nsScriptSecurityManager::CheckSameOriginURI  where we directly compare the origins for the two requests.

Differential Revision: https://phabricator.services.mozilla.com/D170868
This commit is contained in:
sunil mayya 2023-02-28 12:06:36 +00:00
parent fb969de225
commit 3ae22e6d6f
5 changed files with 68 additions and 16 deletions

View File

@ -1540,7 +1540,20 @@ FetchDriver::AsyncOnChannelRedirect(nsIChannel* aOldChannel,
// Ref: https://fetch.spec.whatwg.org/#http-redirect-fetch
bool skipAuthHeader = false;
if (StaticPrefs::network_fetch_redirect_stripAuthHeader()) {
skipAuthHeader = ReferrerInfo::IsCrossOriginRequest(oldHttpChannel);
nsCOMPtr<nsIURI> oldUri;
MOZ_ALWAYS_SUCCEEDS(
NS_GetFinalChannelURI(aOldChannel, getter_AddRefs(oldUri)));
nsCOMPtr<nsIURI> newUri;
MOZ_ALWAYS_SUCCEEDS(
NS_GetFinalChannelURI(aNewChannel, getter_AddRefs(newUri)));
nsresult rv = nsContentUtils::GetSecurityManager()->CheckSameOriginURI(
newUri, oldUri, false, false);
if (NS_FAILED(rv)) {
skipAuthHeader = true;
}
}
SetRequestHeaders(newHttpChannel, rewriteToGET, skipAuthHeader);

View File

@ -3361,7 +3361,20 @@ nsresult XMLHttpRequestMainThread::OnRedirectVerifyCallback(nsresult result) {
// Ref: https://fetch.spec.whatwg.org/#http-redirect-fetch
bool skipAuthHeader = false;
if (StaticPrefs::network_fetch_redirect_stripAuthHeader()) {
skipAuthHeader = ReferrerInfo::IsCrossOriginRequest(oldHttpChannel);
nsCOMPtr<nsIURI> oldUri;
MOZ_ALWAYS_SUCCEEDS(
NS_GetFinalChannelURI(oldHttpChannel, getter_AddRefs(oldUri)));
nsCOMPtr<nsIURI> newUri;
MOZ_ALWAYS_SUCCEEDS(
NS_GetFinalChannelURI(newHttpChannel, getter_AddRefs(newUri)));
nsresult rv = nsContentUtils::GetSecurityManager()->CheckSameOriginURI(
newUri, oldUri, false, false);
if (NS_FAILED(rv)) {
skipAuthHeader = true;
}
}
// Ensure all original headers are duplicated for the new channel (bug

View File

@ -4933,13 +4933,35 @@ nsresult HttpBaseChannel::SetupReplacementChannel(nsIURI* newURI,
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));
if (!IsNewChannelSameOrigin(httpChannel)) {
rv = httpChannel->SetRequestHeader("Authorization"_ns, ""_ns, false);
MOZ_ASSERT(NS_SUCCEEDED(rv));
}
}
return NS_OK;
}
// check whether the new channel is of same origin as the current channel
bool HttpBaseChannel::IsNewChannelSameOrigin(nsIChannel* aNewChannel) {
bool isSameOrigin = false;
nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
if (!ssm) {
return false;
}
nsCOMPtr<nsIURI> newURI;
NS_GetFinalChannelURI(aNewChannel, getter_AddRefs(newURI));
nsresult rv = ssm->CheckSameOriginURI(newURI, mURI, false, false);
if (NS_SUCCEEDED(rv)) {
isSameOrigin = true;
}
return isSameOrigin;
}
bool HttpBaseChannel::ShouldTaintReplacementChannelOrigin(
nsIChannel* aNewChannel, uint32_t aRedirectFlags) {
if (LoadTaintedOriginFlag()) {
@ -4951,19 +4973,13 @@ bool HttpBaseChannel::ShouldTaintReplacementChannelOrigin(
return false;
}
nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
if (!ssm) {
return true;
}
nsCOMPtr<nsIURI> newURI;
NS_GetFinalChannelURI(aNewChannel, getter_AddRefs(newURI));
nsresult rv = ssm->CheckSameOriginURI(newURI, mURI, false, false);
if (NS_SUCCEEDED(rv)) {
// If new channel is not of same origin we need to taint unless
// mURI <-> mOriginalURI/LoadingPrincipal are same origin.
if (IsNewChannelSameOrigin(aNewChannel)) {
return false;
}
// If newURI <-> mURI are not same-origin we need to taint unless
// mURI <-> mOriginalURI/LoadingPrincipal are same origin.
nsresult rv;
if (mLoadInfo->GetLoadingPrincipal()) {
bool sameOrigin = false;
@ -4977,6 +4993,11 @@ bool HttpBaseChannel::ShouldTaintReplacementChannelOrigin(
return true;
}
nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
if (!ssm) {
return true;
}
rv = ssm->CheckSameOriginURI(mOriginalURI, mURI, false, false);
return NS_FAILED(rv);
}

View File

@ -577,6 +577,8 @@ class HttpBaseChannel : public nsHashPropertyBag,
[[nodiscard]] virtual nsresult SetupReplacementChannel(
nsIURI*, nsIChannel*, bool preserveMethod, uint32_t redirectFlags);
bool IsNewChannelSameOrigin(nsIChannel* aNewChannel);
// WHATWG Fetch Standard 4.4. HTTP-redirect fetch, step 10
virtual bool ShouldTaintReplacementChannelOrigin(nsIChannel* aNewChannel,
uint32_t aRedirectFlags);

View File

@ -16,7 +16,10 @@ promise_test(async test => {
}, "getAuthorizationHeaderValue - no redirection");
promise_test(async test => {
const result = await getAuthorizationHeaderValue("/fetch/api/resources/redirect.py?location=" + encodeURIComponent("/fetch/api/resources/dump-authorization-header.py"));
result = await getAuthorizationHeaderValue("/fetch/api/resources/redirect.py?location=" + encodeURIComponent("/fetch/api/resources/dump-authorization-header.py"));
assert_equals(result, authorizationValue);
result = await getAuthorizationHeaderValue(get_host_info().HTTPS_REMOTE_ORIGIN + "/fetch/api/resources/redirect.py?allow_headers=Authorization&location=" + encodeURIComponent(get_host_info().HTTPS_REMOTE_ORIGIN + "/fetch/api/resources/dump-authorization-header.py"));
assert_equals(result, authorizationValue);
}, "getAuthorizationHeaderValue - same origin redirection");