Bug 1725800 - Also check if the uri path is the same when doing upgrade and downgrade loop check, r=ckerschb

Differential Revision: https://phabricator.services.mozilla.com/D122847
This commit is contained in:
Kershaw Chang 2021-08-20 19:29:46 +00:00
parent 360dba0b1c
commit 17d46e745c
9 changed files with 116 additions and 12 deletions

View File

@ -273,6 +273,25 @@ bool nsHTTPSOnlyUtils::IsUpgradeDowngradeEndlessLoop(
nsAutoCString uriHost;
aURI->GetAsciiHost(uriHost);
auto uriAndPrincipalComparator = [&](nsIPrincipal* aPrincipal) {
nsAutoCString principalHost;
aPrincipal->GetAsciiHost(principalHost);
bool checkPath = mozilla::StaticPrefs::
dom_security_https_only_check_path_upgrade_downgrade_endless_loop();
if (!checkPath) {
return uriHost.Equals(principalHost);
}
nsAutoCString uriPath;
nsresult rv = aURI->GetFilePath(uriPath);
if (NS_FAILED(rv)) {
return false;
}
nsAutoCString principalPath;
aPrincipal->GetFilePath(principalPath);
return uriHost.Equals(principalHost) && uriPath.Equals(principalPath);
};
// 6. Check actual redirects. If the Principal that kicked off the
// load/redirect is not https, then it's definitely not a redirect cause by
// https-only. If the scheme of the principal however is https and the
@ -283,14 +302,11 @@ bool nsHTTPSOnlyUtils::IsUpgradeDowngradeEndlessLoop(
nsCOMPtr<nsIPrincipal> redirectPrincipal;
for (nsIRedirectHistoryEntry* entry : aLoadInfo->RedirectChain()) {
entry->GetPrincipal(getter_AddRefs(redirectPrincipal));
if (redirectPrincipal && redirectPrincipal->SchemeIs("https")) {
nsAutoCString redirectHost;
redirectPrincipal->GetAsciiHost(redirectHost);
if (uriHost.Equals(redirectHost)) {
if (redirectPrincipal && redirectPrincipal->SchemeIs("https") &&
uriAndPrincipalComparator(redirectPrincipal)) {
return true;
}
}
}
} else {
// 6.1 We should only check if this load is triggered by a user gesture
// when the redirect chain is empty, since this information is only useful
@ -311,9 +327,8 @@ bool nsHTTPSOnlyUtils::IsUpgradeDowngradeEndlessLoop(
if (!triggeringPrincipal->SchemeIs("https")) {
return false;
}
nsAutoCString triggeringHost;
triggeringPrincipal->GetAsciiHost(triggeringHost);
return uriHost.Equals(triggeringHost);
return uriAndPrincipalComparator(triggeringPrincipal);
}
/* static */

View File

@ -1,7 +1,7 @@
"use strict";
const REDIRECT_URI = "http://example.com/tests/dom/security/test/https-first/file_break_endless_upgrade_downgrade_loop.sjs?verify";
const DOWNGRADE_URI = "http://example.com/tests/dom/security/test/https-first/file_downgrade_with_different_path.sjs";
const RESPONSE_ERROR = "unexpected-query";
// An onload postmessage to window opener
@ -27,6 +27,14 @@ function handleRequest(request, response) {
response.setHeader("Cache-Control", "no-cache", false);
const query = request.queryString;
if (query == "downgrade") {
// send same-origin downgrade from https: to http: with a different path.
// we don't consider it's an endless upgrade downgrade loop in this case.
response.setStatusLine(request.httpVersion, 302, "Found");
response.setHeader("Location", DOWNGRADE_URI, false);
return;
}
// handle the redirect case
if ((query >= 301 && query <= 303) || query == 307) {
// send same-origin downgrade from https: to http: again simluating

View File

@ -0,0 +1,29 @@
"use strict";
// An onload postmessage to window opener
const RESPONSE_HTTPS_SCHEME = `
<html>
<body>
<script type="application/javascript">
window.opener.postMessage({result: 'scheme-https'}, '*');
</script>
</body>
</html>`;
const RESPONSE_HTTP_SCHEME = `
<html>
<body>
<script type="application/javascript">
window.opener.postMessage({result: 'scheme-http'}, '*');
</script>
</body>
</html>`;
function handleRequest(request, response) {
let response_content = request.scheme === "https"
? RESPONSE_HTTPS_SCHEME
: RESPONSE_HTTP_SCHEME;
response.setStatusLine(request.httpVersion, 200, "OK");
response.write(response_content);
return;
}

View File

@ -26,6 +26,7 @@ support-files= file_referrer_policy.sjs
[test_break_endless_upgrade_downgrade_loop.html]
support-files =
file_break_endless_upgrade_downgrade_loop.sjs
file_downgrade_with_different_path.sjs
[test_multiple_redirection.html]
support-files =
file_multiple_redirection.sjs

View File

@ -39,6 +39,17 @@ Test that same origin redirect does not cause endless loop with https-first enab
return;
}
window.removeEventListener("message", receiveMessage);
window.addEventListener("message", receiveMessageForDifferentPathTest);
testDifferentPath();
}
async function receiveMessageForDifferentPathTest(event) {
is(event.data.result,
"scheme-https",
"scheme should be https when the path is different"
);
testWin.close();
window.removeEventListener("message", receiveMessageForDifferentPathTest);
SimpleTest.finish();
}
@ -50,11 +61,19 @@ Test that same origin redirect does not cause endless loop with https-first enab
testWin = window.open(uri);
}
async function testDifferentPath() {
// Load an https:// window which gets downgraded to http://
let uri =
`https://example.com/tests/dom/security/test/https-first/file_break_endless_upgrade_downgrade_loop.sjs?downgrade`;
testWin = window.open(uri);
}
// Set preference and start test
SpecialPowers.pushPrefEnv({ set: [
["dom.security.https_first", true],
["security.mixed_content.block_active_content", false],
["security.mixed_content.block_display_content", false],
["dom.security.https_only_check_path_upgrade_downgrade_endless_loop", true],
]}, startTest);
</script>
</body>

View File

@ -25,6 +25,9 @@ const REDIRECT_JS = `
const REDIRECT_302 =
"http://example.com/tests/dom/security/test/https-only/file_break_endless_upgrade_downgrade_loop.sjs?test3b";
const REDIRECT_302_DIFFERENT_PATH =
"http://example.com/tests/dom/security/test/https-only/file_user_gesture.html";
function handleRequest(request, response) {
// avoid confusing cache behaviour
response.setHeader("Cache-Control", "no-cache", false);
@ -50,6 +53,12 @@ function handleRequest(request, response) {
response.setHeader("Location", REDIRECT_302, false);
return;
}
if (query === "test4a") {
response.setStatusLine("1.1", 302, "Found");
response.setHeader("Location", REDIRECT_302_DIFFERENT_PATH, false);
return;
}
}
// we should never get here, just in case,

View File

@ -18,7 +18,9 @@ support-files = file_http_background_request.sjs
support-files = file_http_background_auth_request.sjs
[test_break_endless_upgrade_downgrade_loop.html]
skip-if = (toolkit == 'android') # no support for error pages, Bug 1697866
support-files = file_break_endless_upgrade_downgrade_loop.sjs
support-files =
file_break_endless_upgrade_downgrade_loop.sjs
file_user_gesture.html
[test_user_suggestion_box.html]
support-files = file_user_suggestion_box.sjs
skip-if = toolkit == 'android' # no https-only errorpage support in android

View File

@ -39,10 +39,19 @@ async function verifyResult(aTestName) {
ok(innerHTML.includes(errorPageL10nId), "the error page should be shown for " + aTestName);
}
async function verifyTest4Result() {
let pathname = content.document.location.pathname;
ok(
pathname === "/tests/dom/security/test/https-only/file_user_gesture.html",
"the http:// page should be loaded"
);
}
async function runTests() {
await SpecialPowers.pushPrefEnv({ set: [
["dom.security.https_only_mode", true],
["dom.security.https_only_mode_break_upgrade_downgrade_endless_loop", true],
["dom.security.https_only_check_path_upgrade_downgrade_endless_loop", true],
]});
// Test 1: Meta Refresh Redirect
@ -51,6 +60,8 @@ async function runTests() {
let winTest2 = window.open(REQUEST_URL + "?test2a", "_blank");
// Test 3: 302 Redirect
let winTest3 = window.open(REQUEST_URL + "?test3a", "_blank");
// Test 4: 302 Redirect with a different path
let winTest4 = window.open(REQUEST_URL + "?test4a", "_blank");
// provide enough time for:
// the redirects to occur, and the error page to be displayed
@ -65,6 +76,9 @@ async function runTests() {
await SpecialPowers.spawn(winTest3, ["test3"], verifyResult);
winTest3.close();
await SpecialPowers.spawn(winTest4, ["test4"], verifyTest4Result);
winTest4.close();
SimpleTest.finish();
}

View File

@ -3086,6 +3086,13 @@
value: true
mirror: always
# If true, when checking if it's upgrade downgrade cycles, the URI path will be
# also checked.
- name: dom.security.https_only_check_path_upgrade_downgrade_endless_loop
type: RelaxedAtomicBool
value: true
mirror: always
# If true and HTTPS-only mode is enabled, requests
# to local IP addresses are also upgraded
- name: dom.security.https_only_mode.upgrade_local