Bug 1800990 - Don't taint redirected channel for HSTS upgrade, r=robwu,necko-reviewers,tschuster

Differential Revision: https://phabricator.services.mozilla.com/D163305
This commit is contained in:
Kershaw Chang 2022-12-01 13:36:14 +00:00
Родитель ce78234f5e
Коммит 09884ffa96
6 изменённых файлов: 73 добавлений и 10 удалений

Просмотреть файл

@ -4787,7 +4787,7 @@ nsresult HttpBaseChannel::SetupReplacementChannel(nsIURI* newURI,
realChannel->SetTopWindowURI(mTopWindowURI);
realChannel->StoreTaintedOriginFlag(
ShouldTaintReplacementChannelOrigin(newURI));
ShouldTaintReplacementChannelOrigin(newChannel, redirectFlags));
}
// update the DocumentURI indicator since we are being redirected.
@ -4864,20 +4864,29 @@ nsresult HttpBaseChannel::SetupReplacementChannel(nsIURI* newURI,
return NS_OK;
}
bool HttpBaseChannel::ShouldTaintReplacementChannelOrigin(nsIURI* aNewURI) {
bool HttpBaseChannel::ShouldTaintReplacementChannelOrigin(
nsIChannel* aNewChannel, uint32_t aRedirectFlags) {
if (LoadTaintedOriginFlag()) {
return true;
}
if (NS_IsInternalSameURIRedirect(this, aNewChannel, aRedirectFlags) ||
NS_IsHSTSUpgradeRedirect(this, aNewChannel, aRedirectFlags)) {
return false;
}
nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
if (!ssm) {
return true;
}
nsresult rv = ssm->CheckSameOriginURI(aNewURI, mURI, false, false);
nsCOMPtr<nsIURI> newURI;
NS_GetFinalChannelURI(aNewChannel, getter_AddRefs(newURI));
nsresult rv = ssm->CheckSameOriginURI(newURI, mURI, false, false);
if (NS_SUCCEEDED(rv)) {
return false;
}
// If aNewURI <-> mURI are not same-origin we need to taint unless
// If newURI <-> mURI are not same-origin we need to taint unless
// mURI <-> mOriginalURI/LoadingPrincipal are same origin.
if (mLoadInfo->GetLoadingPrincipal()) {

Просмотреть файл

@ -570,7 +570,8 @@ class HttpBaseChannel : public nsHashPropertyBag,
nsIURI*, nsIChannel*, bool preserveMethod, uint32_t redirectFlags);
// WHATWG Fetch Standard 4.4. HTTP-redirect fetch, step 10
virtual bool ShouldTaintReplacementChannelOrigin(nsIURI* aNewURI);
virtual bool ShouldTaintReplacementChannelOrigin(nsIChannel* aNewChannel,
uint32_t aRedirectFlags);
// bundle calling OMR observers and marking flag into one function
inline void CallOnModifyRequestObservers() {

Просмотреть файл

@ -142,7 +142,8 @@ class TRRServiceChannel : public HttpBaseChannel,
nsIURI* aNewURI, nsIChannel* aNewChannel, bool aPreserveMethod,
uint32_t aRedirectFlags) override;
// Skip this check for TRRServiceChannel.
virtual bool ShouldTaintReplacementChannelOrigin(nsIURI* aNewURI) override {
virtual bool ShouldTaintReplacementChannelOrigin(
nsIChannel* aNewChannel, uint32_t aRedirectFlags) override {
return false;
}
virtual bool SameOriginWithOriginalUri(nsIURI* aURI) override;

Просмотреть файл

@ -18,6 +18,12 @@ const { TestUtils } = ChromeUtils.importESModule(
"resource://testing-common/TestUtils.sys.mjs"
);
const ReferrerInfo = Components.Constructor(
"@mozilla.org/referrer-info;1",
"nsIReferrerInfo",
"init"
);
add_setup(async function setup() {
trr_test_setup();
@ -307,3 +313,43 @@ add_task(async function testHttpRequestBlocked() {
dnsRequestObserver.unregister();
await new Promise(resolve => httpserv.stop(resolve));
});
function createPrincipal(url) {
return Services.scriptSecurityManager.createContentPrincipal(
Services.io.newURI(url),
{}
);
}
// Test if the Origin header stays the same after an internal HTTPS upgrade
// caused by HTTPS RR.
add_task(async function testHTTPSRRUpgradeWithOriginHeader() {
dns.clearCache(true);
const url = "http://test.httpssvc.com:80/origin_header";
const originURL = "http://example.com";
let chan = Services.io
.newChannelFromURIWithProxyFlags(
Services.io.newURI(url),
null,
Ci.nsIProtocolProxyService.RESOLVE_ALWAYS_TUNNEL,
null,
createPrincipal(originURL),
createPrincipal(url),
Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL,
Ci.nsIContentPolicy.TYPE_DOCUMENT
)
.QueryInterface(Ci.nsIHttpChannel);
chan.referrerInfo = new ReferrerInfo(
Ci.nsIReferrerInfo.EMPTY,
true,
NetUtil.newURI(url)
);
chan.setRequestHeader("Origin", originURL, false);
let [req, buf] = await channelOpenPromise(chan);
req.QueryInterface(Ci.nsIHttpChannel);
Assert.equal(req.getResponseHeader("x-connection-http2"), "yes");
Assert.equal(buf, originURL);
});

Просмотреть файл

@ -1851,6 +1851,14 @@ function handleRequest(req, res) {
res.writeHead(200);
res.end("");
return;
} else if (u.pathname === "/origin_header") {
let originHeader = req.headers.origin;
res.setHeader("Content-Length", originHeader.length);
res.setHeader("Content-Type", "text/plain");
res.writeHead(200);
res.write(originHeader);
res.end();
return;
}
res.setHeader("Content-Type", "text/html");

Просмотреть файл

@ -257,10 +257,8 @@ add_task(async function upgradeScheme_declarativeNetRequestWithHostAccess() {
// https://github.com/w3c/webappsec-upgrade-insecure-requests/issues/32
Assert.equal(
(await contentFetch("http://dummy/", "http://redir/never_reached")).url,
// TODO bug 1800990: despite the mirrored Origin in ACAO, the CORS check
// fails after a request is upgraded. Once fixed, update this expectation:
undefined, // Should be: "http://dummy/cors_202?from_https",
"TODO 1800990: upgradeScheme + host access should upgrade (cross-origin request)"
"http://dummy/cors_202?from_https",
"upgradeScheme + host access should upgrade (cross-origin request)"
);
// The DNR extension does not have example.net in host_permissions.