Loading dom/security/test/https-only/browser.ini +3 −0 Original line number Diff line number Diff line Loading @@ -16,3 +16,6 @@ support-files = [browser_user_gesture.js] support-files = file_user_gesture.html [browser_hsts_host.js] support-files = hsts_headers.sjs dom/security/test/https-only/browser_hsts_host.js 0 → 100644 +111 −0 Original line number Diff line number Diff line // 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"; // Set bools to track that tests ended. let readMessage = false; let testFinished = false; // 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(onNewMessage); 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(() => testFinished); // Clean up Services.console.unregisterListener(onNewMessage); }); add_task(async function() { // Reset HSTS header readMessage = false; let clearHstsUrl = getRootDirectory(gTestPath).replace( "chrome://mochitests/content", "https://example.com" ) + "hsts_headers.sjs?reset"; Services.obs.addObserver(observer, "http-on-examine-response"); // reset hsts header await BrowserTestUtils.loadURI(gBrowser.selectedBrowser, clearHstsUrl); await BrowserTestUtils.waitForCondition(() => readMessage); // Clean up Services.obs.removeObserver(observer, "http-on-examine-response"); }); 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); if (channel.URI.spec.includes("reset")) { try { let hsts = channel.getResponseHeader("Strict-Transport-Security"); is(hsts, "max-age=0", "HSTS header is not set"); } catch (e) { ok(false, "HSTS header still set"); } readMessage = true; return; } 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=60", "HSTS header is set"); is(csp, "upgrade-insecure-requests", "CSP header is set"); } catch (e) { ok(false, "No header set"); } readMessage = true; } function onNewMessage(msgObj) { const message = msgObj.message; // ensure that request is not upgraded HTTPS-Only. if (message.includes("Upgrading insecure request")) { ok(false, "Top-Level upgrade shouldn't get logged"); testFinished = true; } else if (gBrowser.selectedBrowser.currentURI.scheme === "https") { ok(true, "Top-Level upgrade shouldn't get logged"); testFinished = true; } } dom/security/test/https-only/hsts_headers.sjs 0 → 100644 +24 −0 Original line number Diff line number Diff line /* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ */ function handleRequest(request, response) { if (request.queryString === "reset") { // Reset the HSTS policy, prevent influencing other tests response.setStatusLine(request.httpVersion, 200, "OK"); response.setHeader("Strict-Transport-Security", "max-age=0"); response.write("Resetting HSTS"); return; } let hstsHeader = "max-age=60"; 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("<!DOCTYPE html><html><body><h1>Ok!</h1></body></html>"); } netwerk/base/nsNetUtil.cpp +200 −154 Original line number Diff line number Diff line Loading @@ -2827,41 +2827,42 @@ bool NS_IsSrcdocChannel(nsIChannel* aChannel) { return false; } nsresult NS_ShouldSecureUpgrade( nsIURI* aURI, nsILoadInfo* aLoadInfo, nsIPrincipal* aChannelResultPrincipal, bool aPrivateBrowsing, bool aAllowSTS, const OriginAttributes& aOriginAttributes, bool& aShouldUpgrade, std::function<void(bool, nsresult)>&& aResultCallback, bool& aWillCallback) { MOZ_ASSERT(XRE_IsParentProcess()); if (!XRE_IsParentProcess()) { return NS_ERROR_NOT_AVAILABLE; // helper function for NS_ShouldSecureUpgrade for checking HSTS bool handleResultFunc(bool aAllowSTS, bool aIsStsHost, uint32_t aHstsSource) { if (aIsStsHost) { LOG(("nsHttpChannel::Connect() STS permissions found\n")); if (aAllowSTS) { Telemetry::AccumulateCategorical( Telemetry::LABELS_HTTP_SCHEME_UPGRADE_TYPE::STS); switch (aHstsSource) { case nsISiteSecurityService::SOURCE_PRELOAD_LIST: Telemetry::Accumulate(Telemetry::HSTS_UPGRADE_SOURCE, 0); break; case nsISiteSecurityService::SOURCE_ORGANIC_REQUEST: Telemetry::Accumulate(Telemetry::HSTS_UPGRADE_SOURCE, 1); break; case nsISiteSecurityService::SOURCE_UNKNOWN: default: // record this as an organic request Telemetry::Accumulate(Telemetry::HSTS_UPGRADE_SOURCE, 1); break; } aWillCallback = false; // Even if we're in private browsing mode, we still enforce existing STS // data (it is read-only). // if the connection is not using SSL and either the exact host matches or // a superdomain wants to force HTTPS, do it. bool isHttps = aURI->SchemeIs("https"); if (!isHttps && !nsMixedContentBlocker::IsPotentiallyTrustworthyLoopbackURL(aURI)) { 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; return true; } // 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()) { Telemetry::AccumulateCategorical( Telemetry::LABELS_HTTP_SCHEME_UPGRADE_TYPE::PrefBlockedSTS); } else { Telemetry::AccumulateCategorical( Telemetry::LABELS_HTTP_SCHEME_UPGRADE_TYPE::NoReasonToUpgrade); } 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); Loading @@ -2869,28 +2870,35 @@ nsresult NS_ShouldSecureUpgrade( scheme.AppendLiteral("s"); NS_ConvertUTF8toUTF16 reportSpec(aURI->GetSpecOrDefault()); NS_ConvertUTF8toUTF16 reportScheme(scheme); if (aLoadInfo->GetUpgradeInsecureRequests()) { AutoTArray<nsString, 2> params = {reportSpec, reportScheme}; uint32_t innerWindowId = aLoadInfo->GetInnerWindowID(); CSP_LogLocalizedStr( "upgradeInsecureRequest", params, CSP_LogLocalizedStr("upgradeInsecureRequest", params, u""_ns, // aSourceFile u""_ns, // aScriptSample 0, // aLineNumber 0, // aColumnNumber nsIScriptError::warningFlag, "upgradeInsecureRequest"_ns, innerWindowId, nsIScriptError::warningFlag, "upgradeInsecureRequest"_ns, innerWindowId, !!aLoadInfo->GetOriginAttributes().mPrivateBrowsingId); Telemetry::AccumulateCategorical( Telemetry::LABELS_HTTP_SCHEME_UPGRADE_TYPE::CSP); } else { 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<nsString, 2> params = {reportSpec, reportScheme}; nsAutoString localizedMsg; nsContentUtils::FormatLocalizedString( nsContentUtils::eSECURITY_PROPERTIES, "MixedContentAutoUpgrade", params, localizedMsg); nsContentUtils::FormatLocalizedString(nsContentUtils::eSECURITY_PROPERTIES, "MixedContentAutoUpgrade", params, localizedMsg); // Prepending ixed Content to the outgoing console message nsString message; Loading @@ -2907,12 +2915,63 @@ nsresult NS_ShouldSecureUpgrade( aLoadInfo->SetBrowserDidUpgradeInsecureRequests(true); Telemetry::AccumulateCategorical( Telemetry::LABELS_HTTP_SCHEME_UPGRADE_TYPE::BrowserDisplay); return true; } aShouldUpgrade = true; // 4. Https-Only / -First if (nsHTTPSOnlyUtils::ShouldUpgradeRequest(aURI, aLoadInfo) || nsHTTPSOnlyUtils::ShouldUpgradeHttpsFirstRequest(aURI, aLoadInfo)) { return true; } return false; } // 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, const OriginAttributes& aOriginAttributes, bool& aShouldUpgrade, std::function<void(bool, nsresult)>&& aResultCallback, bool& aWillCallback) { MOZ_ASSERT(XRE_IsParentProcess()); if (!XRE_IsParentProcess()) { return NS_ERROR_NOT_AVAILABLE; } aWillCallback = false; aShouldUpgrade = false; // Even if we're in private browsing mode, we still enforce existing STS // data (it is read-only). // if the connection is not using SSL and either the exact host matches or // a superdomain wants to force HTTPS, do it. bool isHttps = aURI->SchemeIs("https"); // If request is https, then there is nothing to do here. if (isHttps) { Telemetry::AccumulateCategorical( Telemetry::LABELS_HTTP_SCHEME_UPGRADE_TYPE::AlreadyHTTPS); aShouldUpgrade = false; return NS_OK; } // If it is a mixed content trustworthy loopback, then we shouldn't upgrade // it. if (nsMixedContentBlocker::IsPotentiallyTrustworthyLoopbackURL(aURI)) { 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")); // enforce Strict-Transport-Security nsISiteSecurityService* sss = gHttpHandler->GetSSService(); Loading @@ -2922,43 +2981,29 @@ nsresult NS_ShouldSecureUpgrade( uint32_t hstsSource = 0; uint32_t flags = aPrivateBrowsing ? nsISocketProvider::NO_PERMANENT_STORAGE : 0; auto handleResultFunc = [aAllowSTS](bool aIsStsHost, uint32_t aHstsSource) { if (aIsStsHost) { LOG(("nsHttpChannel::Connect() STS permissions found\n")); if (aAllowSTS) { Telemetry::AccumulateCategorical( Telemetry::LABELS_HTTP_SCHEME_UPGRADE_TYPE::STS); switch (aHstsSource) { case nsISiteSecurityService::SOURCE_PRELOAD_LIST: Telemetry::Accumulate(Telemetry::HSTS_UPGRADE_SOURCE, 0); break; case nsISiteSecurityService::SOURCE_ORGANIC_REQUEST: Telemetry::Accumulate(Telemetry::HSTS_UPGRADE_SOURCE, 1); break; case nsISiteSecurityService::SOURCE_UNKNOWN: default: // record this as an organic request Telemetry::Accumulate(Telemetry::HSTS_UPGRADE_SOURCE, 1); break; } return true; } Telemetry::AccumulateCategorical( Telemetry::LABELS_HTTP_SCHEME_UPGRADE_TYPE::PrefBlockedSTS); } else { Telemetry::AccumulateCategorical( Telemetry::LABELS_HTTP_SCHEME_UPGRADE_TYPE::NoReasonToUpgrade); } return false; }; // 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<bool, Relaxed> storageReady(false); if (!storageReady && gSocketTransportService && aResultCallback) { nsCOMPtr<nsILoadInfo> loadInfo = aLoadInfo; nsCOMPtr<nsIURI> uri = aURI; auto callbackWrapper = [resultCallback{std::move(aResultCallback)}, uri, loadInfo](bool aShouldUpgrade, nsresult aStatus) { MOZ_ASSERT(NS_IsMainThread()); // 1. HSTS upgrade if (aShouldUpgrade || NS_FAILED(aStatus)) { resultCallback(aShouldUpgrade, aStatus); return; } // 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<nsISiteSecurityService> service = sss; nsresult rv = gSocketTransportService->Dispatch( NS_NewRunnableFunction( Loading @@ -2966,9 +3011,10 @@ nsresult NS_ShouldSecureUpgrade( [service{std::move(service)}, uri{std::move(uri)}, flags(flags), originAttributes(aOriginAttributes), handleResultFunc{std::move(handleResultFunc)}, resultCallback{std::move(aResultCallback)}]() mutable { uint32_t hstsSource = 0; 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); Loading @@ -2976,13 +3022,14 @@ nsresult NS_ShouldSecureUpgrade( // Successfully get the result from |IsSecureURI| implies that // the storage is ready to read. storageReady = NS_SUCCEEDED(rv); bool shouldUpgrade = handleResultFunc(isStsHost, hstsSource); bool shouldUpgrade = handleResultFunc(allowSTS, isStsHost, hstsSource); // Check if request should be upgraded. NS_DispatchToMainThread(NS_NewRunnableFunction( "net::NS_ShouldSecureUpgrade::ResultCallback", [rv, shouldUpgrade, resultCallback{std::move(resultCallback)}]() { resultCallback(shouldUpgrade, rv); callbackWrapper{std::move(callbackWrapper)}]() { callbackWrapper(shouldUpgrade, rv); })); }), NS_DISPATCH_NORMAL); Loading @@ -2998,14 +3045,13 @@ nsresult NS_ShouldSecureUpgrade( // should be reported. NS_ENSURE_SUCCESS(rv, rv); aShouldUpgrade = handleResultFunc(isStsHost, hstsSource); return NS_OK; 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); } Telemetry::AccumulateCategorical( Telemetry::LABELS_HTTP_SCHEME_UPGRADE_TYPE::AlreadyHTTPS); aShouldUpgrade = false; return NS_OK; return rv; } nsresult NS_GetSecureUpgradedURI(nsIURI* aURI, nsIURI** aUpgradedURI) { Loading Loading
dom/security/test/https-only/browser.ini +3 −0 Original line number Diff line number Diff line Loading @@ -16,3 +16,6 @@ support-files = [browser_user_gesture.js] support-files = file_user_gesture.html [browser_hsts_host.js] support-files = hsts_headers.sjs
dom/security/test/https-only/browser_hsts_host.js 0 → 100644 +111 −0 Original line number Diff line number Diff line // 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"; // Set bools to track that tests ended. let readMessage = false; let testFinished = false; // 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(onNewMessage); 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(() => testFinished); // Clean up Services.console.unregisterListener(onNewMessage); }); add_task(async function() { // Reset HSTS header readMessage = false; let clearHstsUrl = getRootDirectory(gTestPath).replace( "chrome://mochitests/content", "https://example.com" ) + "hsts_headers.sjs?reset"; Services.obs.addObserver(observer, "http-on-examine-response"); // reset hsts header await BrowserTestUtils.loadURI(gBrowser.selectedBrowser, clearHstsUrl); await BrowserTestUtils.waitForCondition(() => readMessage); // Clean up Services.obs.removeObserver(observer, "http-on-examine-response"); }); 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); if (channel.URI.spec.includes("reset")) { try { let hsts = channel.getResponseHeader("Strict-Transport-Security"); is(hsts, "max-age=0", "HSTS header is not set"); } catch (e) { ok(false, "HSTS header still set"); } readMessage = true; return; } 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=60", "HSTS header is set"); is(csp, "upgrade-insecure-requests", "CSP header is set"); } catch (e) { ok(false, "No header set"); } readMessage = true; } function onNewMessage(msgObj) { const message = msgObj.message; // ensure that request is not upgraded HTTPS-Only. if (message.includes("Upgrading insecure request")) { ok(false, "Top-Level upgrade shouldn't get logged"); testFinished = true; } else if (gBrowser.selectedBrowser.currentURI.scheme === "https") { ok(true, "Top-Level upgrade shouldn't get logged"); testFinished = true; } }
dom/security/test/https-only/hsts_headers.sjs 0 → 100644 +24 −0 Original line number Diff line number Diff line /* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ */ function handleRequest(request, response) { if (request.queryString === "reset") { // Reset the HSTS policy, prevent influencing other tests response.setStatusLine(request.httpVersion, 200, "OK"); response.setHeader("Strict-Transport-Security", "max-age=0"); response.write("Resetting HSTS"); return; } let hstsHeader = "max-age=60"; 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("<!DOCTYPE html><html><body><h1>Ok!</h1></body></html>"); }
netwerk/base/nsNetUtil.cpp +200 −154 Original line number Diff line number Diff line Loading @@ -2827,41 +2827,42 @@ bool NS_IsSrcdocChannel(nsIChannel* aChannel) { return false; } nsresult NS_ShouldSecureUpgrade( nsIURI* aURI, nsILoadInfo* aLoadInfo, nsIPrincipal* aChannelResultPrincipal, bool aPrivateBrowsing, bool aAllowSTS, const OriginAttributes& aOriginAttributes, bool& aShouldUpgrade, std::function<void(bool, nsresult)>&& aResultCallback, bool& aWillCallback) { MOZ_ASSERT(XRE_IsParentProcess()); if (!XRE_IsParentProcess()) { return NS_ERROR_NOT_AVAILABLE; // helper function for NS_ShouldSecureUpgrade for checking HSTS bool handleResultFunc(bool aAllowSTS, bool aIsStsHost, uint32_t aHstsSource) { if (aIsStsHost) { LOG(("nsHttpChannel::Connect() STS permissions found\n")); if (aAllowSTS) { Telemetry::AccumulateCategorical( Telemetry::LABELS_HTTP_SCHEME_UPGRADE_TYPE::STS); switch (aHstsSource) { case nsISiteSecurityService::SOURCE_PRELOAD_LIST: Telemetry::Accumulate(Telemetry::HSTS_UPGRADE_SOURCE, 0); break; case nsISiteSecurityService::SOURCE_ORGANIC_REQUEST: Telemetry::Accumulate(Telemetry::HSTS_UPGRADE_SOURCE, 1); break; case nsISiteSecurityService::SOURCE_UNKNOWN: default: // record this as an organic request Telemetry::Accumulate(Telemetry::HSTS_UPGRADE_SOURCE, 1); break; } aWillCallback = false; // Even if we're in private browsing mode, we still enforce existing STS // data (it is read-only). // if the connection is not using SSL and either the exact host matches or // a superdomain wants to force HTTPS, do it. bool isHttps = aURI->SchemeIs("https"); if (!isHttps && !nsMixedContentBlocker::IsPotentiallyTrustworthyLoopbackURL(aURI)) { 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; return true; } // 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()) { Telemetry::AccumulateCategorical( Telemetry::LABELS_HTTP_SCHEME_UPGRADE_TYPE::PrefBlockedSTS); } else { Telemetry::AccumulateCategorical( Telemetry::LABELS_HTTP_SCHEME_UPGRADE_TYPE::NoReasonToUpgrade); } 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); Loading @@ -2869,28 +2870,35 @@ nsresult NS_ShouldSecureUpgrade( scheme.AppendLiteral("s"); NS_ConvertUTF8toUTF16 reportSpec(aURI->GetSpecOrDefault()); NS_ConvertUTF8toUTF16 reportScheme(scheme); if (aLoadInfo->GetUpgradeInsecureRequests()) { AutoTArray<nsString, 2> params = {reportSpec, reportScheme}; uint32_t innerWindowId = aLoadInfo->GetInnerWindowID(); CSP_LogLocalizedStr( "upgradeInsecureRequest", params, CSP_LogLocalizedStr("upgradeInsecureRequest", params, u""_ns, // aSourceFile u""_ns, // aScriptSample 0, // aLineNumber 0, // aColumnNumber nsIScriptError::warningFlag, "upgradeInsecureRequest"_ns, innerWindowId, nsIScriptError::warningFlag, "upgradeInsecureRequest"_ns, innerWindowId, !!aLoadInfo->GetOriginAttributes().mPrivateBrowsingId); Telemetry::AccumulateCategorical( Telemetry::LABELS_HTTP_SCHEME_UPGRADE_TYPE::CSP); } else { 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<nsString, 2> params = {reportSpec, reportScheme}; nsAutoString localizedMsg; nsContentUtils::FormatLocalizedString( nsContentUtils::eSECURITY_PROPERTIES, "MixedContentAutoUpgrade", params, localizedMsg); nsContentUtils::FormatLocalizedString(nsContentUtils::eSECURITY_PROPERTIES, "MixedContentAutoUpgrade", params, localizedMsg); // Prepending ixed Content to the outgoing console message nsString message; Loading @@ -2907,12 +2915,63 @@ nsresult NS_ShouldSecureUpgrade( aLoadInfo->SetBrowserDidUpgradeInsecureRequests(true); Telemetry::AccumulateCategorical( Telemetry::LABELS_HTTP_SCHEME_UPGRADE_TYPE::BrowserDisplay); return true; } aShouldUpgrade = true; // 4. Https-Only / -First if (nsHTTPSOnlyUtils::ShouldUpgradeRequest(aURI, aLoadInfo) || nsHTTPSOnlyUtils::ShouldUpgradeHttpsFirstRequest(aURI, aLoadInfo)) { return true; } return false; } // 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, const OriginAttributes& aOriginAttributes, bool& aShouldUpgrade, std::function<void(bool, nsresult)>&& aResultCallback, bool& aWillCallback) { MOZ_ASSERT(XRE_IsParentProcess()); if (!XRE_IsParentProcess()) { return NS_ERROR_NOT_AVAILABLE; } aWillCallback = false; aShouldUpgrade = false; // Even if we're in private browsing mode, we still enforce existing STS // data (it is read-only). // if the connection is not using SSL and either the exact host matches or // a superdomain wants to force HTTPS, do it. bool isHttps = aURI->SchemeIs("https"); // If request is https, then there is nothing to do here. if (isHttps) { Telemetry::AccumulateCategorical( Telemetry::LABELS_HTTP_SCHEME_UPGRADE_TYPE::AlreadyHTTPS); aShouldUpgrade = false; return NS_OK; } // If it is a mixed content trustworthy loopback, then we shouldn't upgrade // it. if (nsMixedContentBlocker::IsPotentiallyTrustworthyLoopbackURL(aURI)) { 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")); // enforce Strict-Transport-Security nsISiteSecurityService* sss = gHttpHandler->GetSSService(); Loading @@ -2922,43 +2981,29 @@ nsresult NS_ShouldSecureUpgrade( uint32_t hstsSource = 0; uint32_t flags = aPrivateBrowsing ? nsISocketProvider::NO_PERMANENT_STORAGE : 0; auto handleResultFunc = [aAllowSTS](bool aIsStsHost, uint32_t aHstsSource) { if (aIsStsHost) { LOG(("nsHttpChannel::Connect() STS permissions found\n")); if (aAllowSTS) { Telemetry::AccumulateCategorical( Telemetry::LABELS_HTTP_SCHEME_UPGRADE_TYPE::STS); switch (aHstsSource) { case nsISiteSecurityService::SOURCE_PRELOAD_LIST: Telemetry::Accumulate(Telemetry::HSTS_UPGRADE_SOURCE, 0); break; case nsISiteSecurityService::SOURCE_ORGANIC_REQUEST: Telemetry::Accumulate(Telemetry::HSTS_UPGRADE_SOURCE, 1); break; case nsISiteSecurityService::SOURCE_UNKNOWN: default: // record this as an organic request Telemetry::Accumulate(Telemetry::HSTS_UPGRADE_SOURCE, 1); break; } return true; } Telemetry::AccumulateCategorical( Telemetry::LABELS_HTTP_SCHEME_UPGRADE_TYPE::PrefBlockedSTS); } else { Telemetry::AccumulateCategorical( Telemetry::LABELS_HTTP_SCHEME_UPGRADE_TYPE::NoReasonToUpgrade); } return false; }; // 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<bool, Relaxed> storageReady(false); if (!storageReady && gSocketTransportService && aResultCallback) { nsCOMPtr<nsILoadInfo> loadInfo = aLoadInfo; nsCOMPtr<nsIURI> uri = aURI; auto callbackWrapper = [resultCallback{std::move(aResultCallback)}, uri, loadInfo](bool aShouldUpgrade, nsresult aStatus) { MOZ_ASSERT(NS_IsMainThread()); // 1. HSTS upgrade if (aShouldUpgrade || NS_FAILED(aStatus)) { resultCallback(aShouldUpgrade, aStatus); return; } // 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<nsISiteSecurityService> service = sss; nsresult rv = gSocketTransportService->Dispatch( NS_NewRunnableFunction( Loading @@ -2966,9 +3011,10 @@ nsresult NS_ShouldSecureUpgrade( [service{std::move(service)}, uri{std::move(uri)}, flags(flags), originAttributes(aOriginAttributes), handleResultFunc{std::move(handleResultFunc)}, resultCallback{std::move(aResultCallback)}]() mutable { uint32_t hstsSource = 0; 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); Loading @@ -2976,13 +3022,14 @@ nsresult NS_ShouldSecureUpgrade( // Successfully get the result from |IsSecureURI| implies that // the storage is ready to read. storageReady = NS_SUCCEEDED(rv); bool shouldUpgrade = handleResultFunc(isStsHost, hstsSource); bool shouldUpgrade = handleResultFunc(allowSTS, isStsHost, hstsSource); // Check if request should be upgraded. NS_DispatchToMainThread(NS_NewRunnableFunction( "net::NS_ShouldSecureUpgrade::ResultCallback", [rv, shouldUpgrade, resultCallback{std::move(resultCallback)}]() { resultCallback(shouldUpgrade, rv); callbackWrapper{std::move(callbackWrapper)}]() { callbackWrapper(shouldUpgrade, rv); })); }), NS_DISPATCH_NORMAL); Loading @@ -2998,14 +3045,13 @@ nsresult NS_ShouldSecureUpgrade( // should be reported. NS_ENSURE_SUCCESS(rv, rv); aShouldUpgrade = handleResultFunc(isStsHost, hstsSource); return NS_OK; 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); } Telemetry::AccumulateCategorical( Telemetry::LABELS_HTTP_SCHEME_UPGRADE_TYPE::AlreadyHTTPS); aShouldUpgrade = false; return NS_OK; return rv; } nsresult NS_GetSecureUpgradedURI(nsIURI* aURI, nsIURI** aUpgradedURI) { Loading