diff --git a/dom/security/test/https-only/browser.ini b/dom/security/test/https-only/browser.ini index e77ba441c022..517317f6cc35 100644 --- a/dom/security/test/https-only/browser.ini +++ b/dom/security/test/https-only/browser.ini @@ -20,6 +20,3 @@ support-files = [browser_user_gesture.js] support-files = file_user_gesture.html -[browser_hsts_host.js] -support-files = - hsts_headers.sjs diff --git a/dom/security/test/https-only/browser_hsts_host.js b/dom/security/test/https-only/browser_hsts_host.js deleted file mode 100644 index ba59ae610940..000000000000 --- a/dom/security/test/https-only/browser_hsts_host.js +++ /dev/null @@ -1,88 +0,0 @@ -// Bug 1722489 - HTTPS-Only Mode - Tests evaluation order -// https://bugzilla.mozilla.org/show_bug.cgi?id=1722489 -// This test ensures that an http request to an hsts host -// gets upgraded by hsts and not by https-only. -"use strict"; - -// Test Cases -// description: Description of what the tests expects. -let tests = [ - { - description: "Top-Level upgrade shouldn't get logged", - }, -]; -let readMessage = false; -function observer(subject, topic, state) { - info("observer called with " + topic); - if (topic == "http-on-examine-response") { - onExamineResponse(subject); - } -} - -function onExamineResponse(subject) { - let channel = subject.QueryInterface(Ci.nsIHttpChannel); - info("onExamineResponse with " + channel.URI.spec); - try { - let hsts = channel.getResponseHeader("Strict-Transport-Security"); - let csp = channel.getResponseHeader("Content-Security-Policy"); - // Check that HSTS and CSP upgrade headers are set - is(hsts, "max-age=300", "HSTS header is set"); - is(csp, "upgrade-insecure-requests", "CSP header is set"); - } catch (e) { - ok(false, "No header set"); - } - readMessage = true; -} -// Visit a secure site that sends an HSTS header to set up the rest of the -// test. -add_task(async function see_hsts_header() { - let setHstsUrl = - getRootDirectory(gTestPath).replace( - "chrome://mochitests/content", - "https://example.com" - ) + "hsts_headers.sjs"; - Services.obs.addObserver(observer, "http-on-examine-response"); - await BrowserTestUtils.loadURI(gBrowser.selectedBrowser, setHstsUrl); - - await BrowserTestUtils.waitForCondition(() => readMessage); - // Clean up - Services.obs.removeObserver(observer, "http-on-examine-response"); -}); - -// Test that HTTPS_Only is not performed if HSTS host is visited. -add_task(async function() { - // A longer timeout is necessary for this test than the plain mochitests - // due to opening a new tab with the web console. - requestLongerTimeout(4); - - // Enable HTTPS-Only Mode and register console-listener - await SpecialPowers.pushPrefEnv({ - set: [["dom.security.https_only_mode", true]], - }); - Services.console.registerListener(on_new_message); - const RESOURCE_LINK = - getRootDirectory(gTestPath).replace( - "chrome://mochitests/content", - "http://example.com" - ) + "hsts_headers.sjs"; - - // 1. Upgrade page to https:// - await BrowserTestUtils.loadURI(gBrowser.selectedBrowser, RESOURCE_LINK); - - await BrowserTestUtils.waitForCondition(() => tests.length === 0); - - // Clean up - Services.console.unregisterListener(on_new_message); -}); - -function on_new_message(msgObj) { - const message = msgObj.message; - // ensure that request is not upgraded HTTPS-Only. - if (message.includes("Upgrading insecure request")) { - ok(false, tests[0].description); - tests.splice(0, 1); - } else if (gBrowser.selectedBrowser.currentURI.scheme === "https") { - ok(true, tests[0].description); - tests.splice(0, 1); - } -} diff --git a/dom/security/test/https-only/hsts_headers.sjs b/dom/security/test/https-only/hsts_headers.sjs deleted file mode 100644 index 9cfca2e1e6af..000000000000 --- a/dom/security/test/https-only/hsts_headers.sjs +++ /dev/null @@ -1,17 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ */ - -function handleRequest(request, response) { - let hstsHeader = "max-age=300"; - response.setHeader("Strict-Transport-Security", hstsHeader); - response.setHeader("Cache-Control", "no-cache", false); - response.setHeader("Content-Type", "text/html", false); - // Set header for csp upgrade - response.setHeader( - "Content-Security-Policy", - "upgrade-insecure-requests", - false - ); - response.setStatusLine(request.httpVersion, 200); - response.write("

Ok!

"); -} diff --git a/netwerk/base/nsNetUtil.cpp b/netwerk/base/nsNetUtil.cpp index 3f9ca5ef04df..309bfe135cac 100644 --- a/netwerk/base/nsNetUtil.cpp +++ b/netwerk/base/nsNetUtil.cpp @@ -2867,7 +2867,7 @@ bool NS_IsSrcdocChannel(nsIChannel* aChannel) { return false; } -// helper function for NS_ShouldSecureUpgrade for checking HSTS +// helper function for NS_ShouldHSTSUpgrade bool handleResultFunc(bool aAllowSTS, bool aIsStsHost, uint32_t aHstsSource) { if (aIsStsHost) { LOG(("nsHttpChannel::Connect() STS permissions found\n")); @@ -2897,82 +2897,75 @@ bool handleResultFunc(bool aAllowSTS, bool aIsStsHost, uint32_t aHstsSource) { } return false; }; -// That function is a helper function of NS_ShouldSecureUpgrade to check if -// CSP upgrade-insecure-requests, Mixed content auto upgrading or HTTPs-Only/- -// First should upgrade the given request. -static bool ShouldSecureUpgradeNoHSTS(nsIURI* aURI, nsILoadInfo* aLoadInfo) { - // 2. CSP upgrade-insecure-requests - if (aLoadInfo->GetUpgradeInsecureRequests()) { - // let's log a message to the console that we are upgrading a request - nsAutoCString scheme; - aURI->GetScheme(scheme); - // append the additional 's' for security to the scheme :-) - scheme.AppendLiteral("s"); - NS_ConvertUTF8toUTF16 reportSpec(aURI->GetSpecOrDefault()); - NS_ConvertUTF8toUTF16 reportScheme(scheme); - AutoTArray params = {reportSpec, reportScheme}; - uint32_t innerWindowId = aLoadInfo->GetInnerWindowID(); - CSP_LogLocalizedStr("upgradeInsecureRequest", params, - u""_ns, // aSourceFile - u""_ns, // aScriptSample - 0, // aLineNumber - 0, // aColumnNumber - nsIScriptError::warningFlag, - "upgradeInsecureRequest"_ns, innerWindowId, - !!aLoadInfo->GetOriginAttributes().mPrivateBrowsingId); - Telemetry::AccumulateCategorical( - Telemetry::LABELS_HTTP_SCHEME_UPGRADE_TYPE::CSP); - return true; - } - // 3. Mixed content auto upgrading - if (aLoadInfo->GetBrowserUpgradeInsecureRequests()) { - // let's log a message to the console that we are upgrading a request - nsAutoCString scheme; - aURI->GetScheme(scheme); - // append the additional 's' for security to the scheme :-) - scheme.AppendLiteral("s"); - NS_ConvertUTF8toUTF16 reportSpec(aURI->GetSpecOrDefault()); - NS_ConvertUTF8toUTF16 reportScheme(scheme); - AutoTArray params = {reportSpec, reportScheme}; - nsAutoString localizedMsg; - nsContentUtils::FormatLocalizedString(nsContentUtils::eSECURITY_PROPERTIES, - "MixedContentAutoUpgrade", params, - localizedMsg); +nsresult NS_ShouldHSTSUpgrade( + nsIURI* aURI, bool aPrivateBrowsing, bool aAllowSTS, + const mozilla::OriginAttributes& aOriginAttributes, bool& aShouldUpgrade, + std::function&& aResultCallback, + bool& aWillCallback) { + MOZ_ASSERT(XRE_IsParentProcess()); + MOZ_ASSERT(!aURI->SchemeIs("https")); - // Prepending ixed Content to the outgoing console message - nsString message; - message.AppendLiteral(u"Mixed Content: "); - message.Append(localizedMsg); + // enforce Strict-Transport-Security + nsISiteSecurityService* sss = gHttpHandler->GetSSService(); + NS_ENSURE_TRUE(sss, NS_ERROR_OUT_OF_MEMORY); - uint32_t innerWindowId = aLoadInfo->GetInnerWindowID(); - nsContentUtils::ReportToConsoleByWindowID( - message, nsIScriptError::warningFlag, "Mixed Content Message"_ns, - innerWindowId, aURI); + bool isStsHost = false; + uint32_t hstsSource = 0; + uint32_t flags = + aPrivateBrowsing ? nsISocketProvider::NO_PERMANENT_STORAGE : 0; - // Set this flag so we know we'll upgrade because of - // 'security.mixed_content.upgrade_display_content'. - aLoadInfo->SetBrowserDidUpgradeInsecureRequests(true); - Telemetry::AccumulateCategorical( - Telemetry::LABELS_HTTP_SCHEME_UPGRADE_TYPE::BrowserDisplay); + // Calling |IsSecureURI| before the storage is ready to read will + // block the main thread. Once the storage is ready, we can call it + // from main thread. + static Atomic storageReady(false); + if (!storageReady && gSocketTransportService && aResultCallback) { + nsCOMPtr uri = aURI; + nsCOMPtr service = sss; + nsresult rv = gSocketTransportService->Dispatch( + NS_NewRunnableFunction( + "net::NS_ShouldSecureUpgrade", + [service{std::move(service)}, uri{std::move(uri)}, flags(flags), + originAttributes(aOriginAttributes), + handleResultFunc{std::move(handleResultFunc)}, + resultCallback{std::move(aResultCallback)}, + allowSTS{std::move(aAllowSTS)}]() mutable { + uint32_t hstsSource = 0; + bool isStsHost = false; + nsresult rv = + service->IsSecureURI(uri, flags, originAttributes, nullptr, + &hstsSource, &isStsHost); - return true; + // Successfully get the result from |IsSecureURI| implies that + // the storage is ready to read. + storageReady = NS_SUCCEEDED(rv); + bool shouldUpgrade = + handleResultFunc(allowSTS, isStsHost, hstsSource); + + NS_DispatchToMainThread(NS_NewRunnableFunction( + "net::NS_ShouldSecureUpgrade::ResultCallback", + [rv, shouldUpgrade, + resultCallback{std::move(resultCallback)}]() { + resultCallback(shouldUpgrade, rv); + })); + }), + NS_DISPATCH_NORMAL); + aWillCallback = NS_SUCCEEDED(rv); + return rv; } - // 4. Https-Only / -First - if (nsHTTPSOnlyUtils::ShouldUpgradeRequest(aURI, aLoadInfo) || - nsHTTPSOnlyUtils::ShouldUpgradeHttpsFirstRequest(aURI, aLoadInfo)) { - return true; - } - return false; + nsresult rv = sss->IsSecureURI(aURI, flags, aOriginAttributes, nullptr, + &hstsSource, &isStsHost); + + // if the SSS check fails, it's likely because this load is on a + // malformed URI or something else in the setup is wrong, so any error + // should be reported. + NS_ENSURE_SUCCESS(rv, rv); + + aShouldUpgrade = handleResultFunc(aAllowSTS, isStsHost, hstsSource); + return NS_OK; } -// Check if channel should be upgraded. check in the following order: -// 1. HSTS -// 2. CSP upgrade-insecure-requests -// 3. Mixed content auto upgrading -// 4. Https-Only / first -// (5. Https RR - will be checked in nsHttpChannel) nsresult NS_ShouldSecureUpgrade( nsIURI* aURI, nsILoadInfo* aLoadInfo, nsIPrincipal* aChannelResultPrincipal, bool aPrivateBrowsing, bool aAllowSTS, @@ -2985,7 +2978,6 @@ nsresult NS_ShouldSecureUpgrade( } aWillCallback = false; - aShouldUpgrade = false; // Even if we're in private browsing mode, we still enforce existing STS // data (it is read-only). @@ -3006,92 +2998,77 @@ nsresult NS_ShouldSecureUpgrade( aShouldUpgrade = false; return NS_OK; } - // If no loadInfo exist there is nothing to upgrade here. - if (!aLoadInfo) { - aShouldUpgrade = false; - return NS_OK; - } - MOZ_ASSERT(!aURI->SchemeIs("https")); + if (aLoadInfo) { + // Check if the request can get upgraded with the HTTPS-Only mode + if (nsHTTPSOnlyUtils::ShouldUpgradeRequest(aURI, aLoadInfo) || + nsHTTPSOnlyUtils::ShouldUpgradeHttpsFirstRequest(aURI, aLoadInfo)) { + aShouldUpgrade = true; + return NS_OK; + } - // enforce Strict-Transport-Security - nsISiteSecurityService* sss = gHttpHandler->GetSSService(); - NS_ENSURE_TRUE(sss, NS_ERROR_OUT_OF_MEMORY); + // If any of the documents up the chain to the root document makes use of + // the CSP directive 'upgrade-insecure-requests', then it's time to + // fulfill the promise to CSP and mixed content blocking to upgrade the + // channel from http to https. + if (aLoadInfo->GetUpgradeInsecureRequests() || + aLoadInfo->GetBrowserUpgradeInsecureRequests()) { + // let's log a message to the console that we are upgrading a request + nsAutoCString scheme; + aURI->GetScheme(scheme); + // append the additional 's' for security to the scheme :-) + scheme.AppendLiteral("s"); + NS_ConvertUTF8toUTF16 reportSpec(aURI->GetSpecOrDefault()); + NS_ConvertUTF8toUTF16 reportScheme(scheme); - bool isStsHost = false; - uint32_t hstsSource = 0; - uint32_t flags = - aPrivateBrowsing ? nsISocketProvider::NO_PERMANENT_STORAGE : 0; - // Calling |IsSecureURI| before the storage is ready to read will - // block the main thread. Once the storage is ready, we can call it - // from main thread. - static Atomic storageReady(false); - if (!storageReady && gSocketTransportService && aResultCallback) { - nsCOMPtr loadInfo = aLoadInfo; - nsCOMPtr uri = aURI; - auto callbackWrapper = [resultCallback{std::move(aResultCallback)}, uri, - loadInfo](bool aShouldUpgrade, nsresult aStatus) { - MOZ_ASSERT(NS_IsMainThread()); + if (aLoadInfo->GetUpgradeInsecureRequests()) { + AutoTArray params = {reportSpec, reportScheme}; + uint32_t innerWindowId = aLoadInfo->GetInnerWindowID(); + CSP_LogLocalizedStr( + "upgradeInsecureRequest", params, + u""_ns, // aSourceFile + u""_ns, // aScriptSample + 0, // aLineNumber + 0, // aColumnNumber + nsIScriptError::warningFlag, "upgradeInsecureRequest"_ns, + innerWindowId, + !!aLoadInfo->GetOriginAttributes().mPrivateBrowsingId); + Telemetry::AccumulateCategorical( + Telemetry::LABELS_HTTP_SCHEME_UPGRADE_TYPE::CSP); + } else { + AutoTArray params = {reportSpec, reportScheme}; - // 1. HSTS upgrade - if (aShouldUpgrade || NS_FAILED(aStatus)) { - resultCallback(aShouldUpgrade, aStatus); - return; + nsAutoString localizedMsg; + nsContentUtils::FormatLocalizedString( + nsContentUtils::eSECURITY_PROPERTIES, "MixedContentAutoUpgrade", + params, localizedMsg); + + // Prepending ixed Content to the outgoing console message + nsString message; + message.AppendLiteral(u"Mixed Content: "); + message.Append(localizedMsg); + + uint32_t innerWindowId = aLoadInfo->GetInnerWindowID(); + nsContentUtils::ReportToConsoleByWindowID( + message, nsIScriptError::warningFlag, "Mixed Content Message"_ns, + innerWindowId, aURI); + + // Set this flag so we know we'll upgrade because of + // 'security.mixed_content.upgrade_display_content'. + aLoadInfo->SetBrowserDidUpgradeInsecureRequests(true); + Telemetry::AccumulateCategorical( + Telemetry::LABELS_HTTP_SCHEME_UPGRADE_TYPE::BrowserDisplay); } - // Check if we need to upgrade because of other reasons. - // 2. CSP upgrade-insecure-requests - // 3. Mixed content auto upgrading - // 4. Https-Only / first - bool shouldUpgrade = ShouldSecureUpgradeNoHSTS(uri, loadInfo); - resultCallback(shouldUpgrade, aStatus); - }; - nsCOMPtr service = sss; - nsresult rv = gSocketTransportService->Dispatch( - NS_NewRunnableFunction( - "net::NS_ShouldSecureUpgrade", - [service{std::move(service)}, uri{std::move(uri)}, flags(flags), - originAttributes(aOriginAttributes), - handleResultFunc{std::move(handleResultFunc)}, - callbackWrapper{std::move(callbackWrapper)}, - allowSTS{std::move(aAllowSTS)}]() mutable { - bool isStsHost = false; - uint32_t hstsSource = 0; - nsresult rv = - service->IsSecureURI(uri, flags, originAttributes, nullptr, - &hstsSource, &isStsHost); - // Successfully get the result from |IsSecureURI| implies that - // the storage is ready to read. - storageReady = NS_SUCCEEDED(rv); - bool shouldUpgrade = - handleResultFunc(allowSTS, isStsHost, hstsSource); - // Check if request should be upgraded. - NS_DispatchToMainThread(NS_NewRunnableFunction( - "net::NS_ShouldSecureUpgrade::ResultCallback", - [rv, shouldUpgrade, - callbackWrapper{std::move(callbackWrapper)}]() { - callbackWrapper(shouldUpgrade, rv); - })); - }), - NS_DISPATCH_NORMAL); - aWillCallback = NS_SUCCEEDED(rv); - return rv; + aShouldUpgrade = true; + return NS_OK; + } + // check if request can be upgraded by hsts + return NS_ShouldHSTSUpgrade(aURI, aPrivateBrowsing, aAllowSTS, + aOriginAttributes, aShouldUpgrade, + std::move(aResultCallback), aWillCallback); } - - nsresult rv = sss->IsSecureURI(aURI, flags, aOriginAttributes, nullptr, - &hstsSource, &isStsHost); - - // if the SSS check fails, it's likely because this load is on a - // malformed URI or something else in the setup is wrong, so any error - // should be reported. - NS_ENSURE_SUCCESS(rv, rv); - - aShouldUpgrade = handleResultFunc(aAllowSTS, isStsHost, hstsSource); - if (!aShouldUpgrade) { - // Check for CSP upgrade-insecure-requests, Mixed content auto upgrading - // and Https-Only / -First. - aShouldUpgrade = ShouldSecureUpgradeNoHSTS(aURI, aLoadInfo); - } - return rv; + aShouldUpgrade = false; + return NS_OK; } nsresult NS_GetSecureUpgradedURI(nsIURI* aURI, nsIURI** aUpgradedURI) {