diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn index 8b46e56642bc7..7901882f05809 100644 --- a/chrome/browser/BUILD.gn +++ b/chrome/browser/BUILD.gn @@ -1709,10 +1709,6 @@ static_library("browser") { "ssl/https_only_mode_upgrade_interceptor.h", "ssl/https_only_mode_upgrade_url_loader.cc", "ssl/https_only_mode_upgrade_url_loader.h", - "ssl/https_upgrades_interceptor.cc", - "ssl/https_upgrades_interceptor.h", - "ssl/https_upgrades_navigation_throttle.cc", - "ssl/https_upgrades_navigation_throttle.h", "ssl/insecure_form/insecure_form_controller_client.cc", "ssl/insecure_form/insecure_form_controller_client.h", "ssl/known_interception_disclosure_infobar_delegate.cc", diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc index c2173f8d1590f..b2a549459fb40 100644 --- a/chrome/browser/about_flags.cc +++ b/chrome/browser/about_flags.cc @@ -8367,10 +8367,6 @@ const FeatureEntry kFeatureEntries[] = { flag_descriptions::kHttpsOnlyModeDescription, kOsDesktop | kOsAndroid, FEATURE_VALUE_TYPE(features::kHttpsOnlyMode)}, - {"https-first-mode-v2", flag_descriptions::kHttpsFirstModeV2Name, - flag_descriptions::kHttpsFirstModeV2Description, kOsDesktop | kOsAndroid, - FEATURE_VALUE_TYPE(features::kHttpsFirstModeV2)}, - {"https-upgrades", flag_descriptions::kHttpsUpgradesName, flag_descriptions::kHttpsUpgradesDescription, kOsDesktop | kOsAndroid, FEATURE_VALUE_TYPE(features::kHttpsUpgrades)}, diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc index 6cc71c96e9ab4..3b0d49489c4f1 100644 --- a/chrome/browser/chrome_content_browser_client.cc +++ b/chrome/browser/chrome_content_browser_client.cc @@ -137,8 +137,6 @@ #include "chrome/browser/ssl/https_defaulted_callbacks.h" #include "chrome/browser/ssl/https_only_mode_navigation_throttle.h" #include "chrome/browser/ssl/https_only_mode_upgrade_interceptor.h" -#include "chrome/browser/ssl/https_upgrades_interceptor.h" -#include "chrome/browser/ssl/https_upgrades_navigation_throttle.h" #include "chrome/browser/ssl/sct_reporting_service.h" #include "chrome/browser/ssl/ssl_client_auth_metrics.h" #include "chrome/browser/ssl/ssl_client_certificate_selector.h" @@ -5061,19 +5059,11 @@ ChromeContentBrowserClient::CreateThrottlesForNavigation( #endif if (profile && profile->GetPrefs()) { - if (base::FeatureList::IsEnabled(features::kHttpsFirstModeV2)) { - MaybeAddThrottle( - HttpsUpgradesNavigationThrottle::MaybeCreateThrottleFor( - handle, std::make_unique(), - profile->GetPrefs()), - &throttles); - } else { - MaybeAddThrottle( - HttpsOnlyModeNavigationThrottle::MaybeCreateThrottleFor( - handle, std::make_unique(), - profile->GetPrefs()), - &throttles); - } + MaybeAddThrottle( + HttpsOnlyModeNavigationThrottle::MaybeCreateThrottleFor( + handle, std::make_unique(), + profile->GetPrefs()), + &throttles); } MaybeAddThrottle(MaybeCreateNavigationAblationThrottle(handle), &throttles); @@ -5902,10 +5892,7 @@ ChromeContentBrowserClient::WillCreateURLLoaderRequestInterceptors( interceptors.push_back( std::make_unique(frame_tree_node_id)); - if (base::FeatureList::IsEnabled(features::kHttpsFirstModeV2)) { - interceptors.push_back( - std::make_unique(frame_tree_node_id)); - } else { + if (base::FeatureList::IsEnabled(features::kHttpsOnlyMode)) { interceptors.push_back( std::make_unique(frame_tree_node_id)); } diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json index a26142da3250a..4804100758656 100644 --- a/chrome/browser/flag-metadata.json +++ b/chrome/browser/flag-metadata.json @@ -4120,14 +4120,6 @@ "owners": [ "shivanisha" ], "expiry_milestone": 95 }, - { - "name": "https-first-mode-v2", - "owners": [ - "cthomp", - "trusty-transport@chromium.org" - ], - "expiry_milestone": 118 - }, { "name": "https-only-mode", "owners": [ "meacer", "trusty-transport@chromium.org" ], diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc index 93c3a796ca958..47358b81439e2 100644 --- a/chrome/browser/flag_descriptions.cc +++ b/chrome/browser/flag_descriptions.cc @@ -1666,10 +1666,6 @@ const char kHttpsOnlyModeDescription[] = "Adds a setting under chrome://settings/security to opt-in to HTTPS-First " "Mode."; -const char kHttpsFirstModeV2Name[] = "HTTPS-First Mode V2"; -const char kHttpsFirstModeV2Description[] = - "Enable rearchitected version of HTTPS-First Mode."; - const char kHttpsUpgradesName[] = "HTTPS Upgrades"; const char kHttpsUpgradesDescription[] = "Enable automatically upgrading all top-level navigations to HTTPS with " diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h index 9d28117341ffa..c71e3020e8043 100644 --- a/chrome/browser/flag_descriptions.h +++ b/chrome/browser/flag_descriptions.h @@ -928,9 +928,6 @@ extern const char kHideShelfControlsInTabletModeDescription[]; extern const char kHttpsOnlyModeName[]; extern const char kHttpsOnlyModeDescription[]; -extern const char kHttpsFirstModeV2Name[]; -extern const char kHttpsFirstModeV2Description[]; - extern const char kHttpsUpgradesName[]; extern const char kHttpsUpgradesDescription[]; diff --git a/chrome/browser/ssl/https_only_mode_browsertest.cc b/chrome/browser/ssl/https_only_mode_browsertest.cc index dc7a4c3518cd8..0e8fc040a62d0 100644 --- a/chrome/browser/ssl/https_only_mode_browsertest.cc +++ b/chrome/browser/ssl/https_only_mode_browsertest.cc @@ -44,8 +44,6 @@ using security_interstitials::https_only_mode::Event; using security_interstitials::https_only_mode::kEventHistogram; -// Tests for the v1 implementation of HTTPS-First Mode. See -// https_upgrade_browsertest.cc for the tests for v2. class HttpsOnlyModeBrowserTest : public InProcessBrowserTest { public: HttpsOnlyModeBrowserTest() = default; diff --git a/chrome/browser/ssl/https_only_mode_controller_client.cc b/chrome/browser/ssl/https_only_mode_controller_client.cc index 4da77532fc113..690225fd55710 100644 --- a/chrome/browser/ssl/https_only_mode_controller_client.cc +++ b/chrome/browser/ssl/https_only_mode_controller_client.cc @@ -9,7 +9,6 @@ #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ssl/https_only_mode_tab_helper.h" #include "chrome/browser/ssl/stateful_ssl_host_state_delegate_factory.h" -#include "chrome/common/chrome_features.h" #include "chrome/common/webui_url_constants.h" #include "components/security_interstitials/content/settings_page_helper.h" #include "components/security_interstitials/content/stateful_ssl_host_state_delegate.h" @@ -58,16 +57,7 @@ void HttpsOnlyModeControllerClient::Proceed() { } auto* tab_helper = HttpsOnlyModeTabHelper::FromWebContents(web_contents_); tab_helper->set_is_navigation_upgraded(false); - - // Proceeding through the interstitial triggers the fallback navigation for - // the initial version of HTTPS-First Mode, but in the new version the - // interstitial is the result of the fallback navigation. Update state - // accordingly. - if (base::FeatureList::IsEnabled(features::kHttpsFirstModeV2)) { - tab_helper->set_is_navigation_fallback(false); - } else { - tab_helper->set_is_navigation_fallback(true); - } + tab_helper->set_is_navigation_fallback(true); web_contents_->GetController().Reload(content::ReloadType::NORMAL, false); // The failed https navigation will remain as a forward entry, so it needs to // be removed. diff --git a/chrome/browser/ssl/https_upgrades_browsertest.cc b/chrome/browser/ssl/https_upgrades_browsertest.cc deleted file mode 100644 index 1c7afd6b027fe..0000000000000 --- a/chrome/browser/ssl/https_upgrades_browsertest.cc +++ /dev/null @@ -1,1044 +0,0 @@ -// Copyright 2022 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include -#include - -#include "base/test/bind.h" -#include "base/test/metrics/histogram_tester.h" -#include "base/test/simple_test_clock.h" -#include "chrome/browser/interstitials/security_interstitial_page_test_utils.h" -#include "chrome/browser/profiles/profile.h" -#include "chrome/browser/ssl/https_only_mode_navigation_throttle.h" -#include "chrome/browser/ssl/https_only_mode_upgrade_interceptor.h" -#include "chrome/browser/ssl/security_state_tab_helper.h" -#include "chrome/browser/ui/browser.h" -#include "chrome/browser/ui/browser_tabstrip.h" -#include "chrome/common/chrome_features.h" -#include "chrome/common/pref_names.h" -#include "chrome/test/base/in_process_browser_test.h" -#include "components/prefs/pref_service.h" -#include "components/security_interstitials/content/stateful_ssl_host_state_delegate.h" -#include "components/security_interstitials/core/https_only_mode_metrics.h" -#include "components/security_interstitials/core/metrics_helper.h" -#include "components/strings/grit/components_strings.h" -#include "components/variations/active_field_trials.h" -#include "components/variations/hashing.h" -#include "content/public/browser/storage_partition.h" -#include "content/public/test/browser_test.h" -#include "content/public/test/browser_test_utils.h" -#include "content/public/test/content_browser_test_utils.h" -#include "content/public/test/content_mock_cert_verifier.h" -#include "content/public/test/test_navigation_observer.h" -#include "net/dns/mock_host_resolver.h" -#include "net/test/cert_test_util.h" -#include "net/test/embedded_test_server/embedded_test_server.h" -#include "net/test/embedded_test_server/http_request.h" -#include "net/test/embedded_test_server/request_handler_util.h" -#include "net/test/test_data_directory.h" -#include "services/network/public/mojom/network_context.mojom.h" -#include "ui/base/l10n/l10n_util.h" -#include "url/url_constants.h" - -using security_interstitials::https_only_mode::Event; -using security_interstitials::https_only_mode::kEventHistogram; - -// Tests for the v2 implementation of HTTPS-First Mode. -class HttpsUpgradesBrowserTest : public InProcessBrowserTest { - public: - HttpsUpgradesBrowserTest() = default; - ~HttpsUpgradesBrowserTest() override = default; - - void SetUp() override { - feature_list_.InitAndEnableFeature(features::kHttpsFirstModeV2); - InProcessBrowserTest::SetUp(); - } - - void SetUpOnMainThread() override { - // By default allow all hosts on HTTPS. - mock_cert_verifier_.mock_cert_verifier()->set_default_result(net::OK); - host_resolver()->AddRule("*", "127.0.0.1"); - - // Set up "bad-https.test" as a hostname with an SSL error. HTTPS upgrades - // to this host will fail. - scoped_refptr cert(https_server_.GetCertificate()); - net::CertVerifyResult verify_result; - verify_result.is_issued_by_known_root = false; - verify_result.verified_cert = cert; - verify_result.cert_status = net::CERT_STATUS_COMMON_NAME_INVALID; - mock_cert_verifier_.mock_cert_verifier()->AddResultForCertAndHost( - cert, "bad-https.test", verify_result, - net::ERR_CERT_COMMON_NAME_INVALID); - - http_server_.AddDefaultHandlers(GetChromeTestDataDir()); - https_server_.AddDefaultHandlers(GetChromeTestDataDir()); - ASSERT_TRUE(http_server_.Start()); - ASSERT_TRUE(https_server_.Start()); - - HttpsOnlyModeUpgradeInterceptor::SetHttpsPortForTesting( - https_server()->port()); - HttpsOnlyModeUpgradeInterceptor::SetHttpPortForTesting( - http_server()->port()); - - SetPref(true); - } - - void TearDownOnMainThread() override { SetPref(false); } - - void SetUpCommandLine(base::CommandLine* command_line) override { - mock_cert_verifier_.SetUpCommandLine(command_line); - } - - void SetUpInProcessBrowserTestFixture() override { - mock_cert_verifier_.SetUpInProcessBrowserTestFixture(); - } - - void TearDownInProcessBrowserTestFixture() override { - mock_cert_verifier_.TearDownInProcessBrowserTestFixture(); - } - - protected: - void SetPref(bool enabled) { - auto* prefs = browser()->profile()->GetPrefs(); - prefs->SetBoolean(prefs::kHttpsOnlyModeEnabled, enabled); - } - - bool GetPref() const { - auto* prefs = browser()->profile()->GetPrefs(); - return prefs->GetBoolean(prefs::kHttpsOnlyModeEnabled); - } - - void ProceedThroughInterstitial(content::WebContents* tab) { - content::TestNavigationObserver nav_observer(tab, 1); - std::string javascript = "window.certificateErrorPageController.proceed();"; - ASSERT_TRUE(content::ExecuteScript(tab, javascript)); - nav_observer.Wait(); - } - - void DontProceedThroughInterstitial(content::WebContents* tab) { - content::TestNavigationObserver nav_observer(tab, 1); - std::string javascript = - "window.certificateErrorPageController.dontProceed();"; - ASSERT_TRUE(content::ExecuteScript(tab, javascript)); - nav_observer.Wait(); - } - - void NavigateAndWaitForFallback(content::WebContents* tab, const GURL& url) { - // Fallback to HTTP (and showing the HTTPS-First Mode interstitial, if - // enabled) is a new navigation, so navigate to the initial URL and wait - // for *two* navigations to complete. - content::NavigateToURLBlockUntilNavigationsComplete(tab, url, 2); - } - - net::EmbeddedTestServer* http_server() { return &http_server_; } - net::EmbeddedTestServer* https_server() { return &https_server_; } - base::HistogramTester* histograms() { return &histograms_; } - - private: - base::test::ScopedFeatureList feature_list_; - net::EmbeddedTestServer http_server_{net::EmbeddedTestServer::TYPE_HTTP}; - net::EmbeddedTestServer https_server_{net::EmbeddedTestServer::TYPE_HTTPS}; - content::ContentMockCertVerifier mock_cert_verifier_; - base::HistogramTester histograms_; -}; - -// If the user navigates to an HTTP URL for a site that supports HTTPS, the -// navigation should end up on the HTTPS version of the URL. -IN_PROC_BROWSER_TEST_F(HttpsUpgradesBrowserTest, - UrlWithHttpScheme_ShouldUpgrade) { - GURL http_url = http_server()->GetURL("foo.test", "/simple.html"); - GURL https_url = https_server()->GetURL("foo.test", "/simple.html"); - - // The NavigateToURL() call returns `false` because the navigation is - // redirected to HTTPS. - auto* contents = browser()->tab_strip_model()->GetActiveWebContents(); - content::TestNavigationObserver nav_observer(contents, 1); - EXPECT_FALSE(content::NavigateToURL(contents, http_url)); - nav_observer.Wait(); - - EXPECT_TRUE(nav_observer.last_navigation_succeeded()); - EXPECT_EQ(https_url, contents->GetLastCommittedURL()); - EXPECT_FALSE(chrome_browser_interstitials::IsShowingInterstitial(contents)); - - // Verify that navigation event metrics were correctly recorded. - histograms()->ExpectTotalCount(kEventHistogram, 2); - histograms()->ExpectBucketCount(kEventHistogram, Event::kUpgradeAttempted, 1); - histograms()->ExpectBucketCount(kEventHistogram, Event::kUpgradeSucceeded, 1); -} - -// If the user navigates to an HTTPS URL for a site that supports HTTPS, the -// navigation should end up on that exact URL. -IN_PROC_BROWSER_TEST_F(HttpsUpgradesBrowserTest, - UrlWithHttpsScheme_ShouldLoad) { - GURL https_url = https_server()->GetURL("foo.test", "/simple.html"); - auto* contents = browser()->tab_strip_model()->GetActiveWebContents(); - EXPECT_TRUE(content::NavigateToURL(contents, https_url)); - - // Verify that navigation event metrics were not recorded as the navigation - // was not upgraded. - histograms()->ExpectTotalCount(kEventHistogram, 0); -} - -// If the user navigates to a localhost URL, the navigation should end up on -// that exact URL. -IN_PROC_BROWSER_TEST_F(HttpsUpgradesBrowserTest, Localhost_ShouldNotUpgrade) { - GURL localhost_url = http_server()->GetURL("localhost", "/simple.html"); - auto* contents = browser()->tab_strip_model()->GetActiveWebContents(); - EXPECT_TRUE(content::NavigateToURL(contents, localhost_url)); - - // Verify that navigation event metrics were not recorded as the navigation - // was not upgraded. - histograms()->ExpectTotalCount(kEventHistogram, 0); -} - -// If the user navigates to an HTTPS URL, the navigation should end up on that -// exact URL, even if the site has an SSL error. -IN_PROC_BROWSER_TEST_F(HttpsUpgradesBrowserTest, - UrlWithHttpsScheme_BrokenSSL_ShouldNotFallback) { - GURL https_url = https_server()->GetURL("bad-https.test", "/simple.html"); - - auto* contents = browser()->tab_strip_model()->GetActiveWebContents(); - EXPECT_FALSE(content::NavigateToURL(contents, https_url)); - EXPECT_EQ(https_url, contents->GetLastCommittedURL()); - EXPECT_TRUE(chrome_browser_interstitials::IsShowingSSLInterstitial(contents)); - - // Verify that navigation event metrics were not recorded as the navigation - // was not upgraded. - histograms()->ExpectTotalCount(kEventHistogram, 0); -} - -// If the user navigates to an HTTP URL for a site with broken HTTPS, the -// navigation should end up on the HTTPS URL and show the HTTPS-Only Mode -// interstitial. -IN_PROC_BROWSER_TEST_F(HttpsUpgradesBrowserTest, - UrlWithHttpScheme_BrokenSSL_ShouldInterstitial) { - GURL http_url = http_server()->GetURL("bad-https.test", "/simple.html"); - GURL https_url = https_server()->GetURL("bad-https.test", "/simple.html"); - - auto* contents = browser()->tab_strip_model()->GetActiveWebContents(); - NavigateAndWaitForFallback(contents, http_url); - EXPECT_EQ(http_url, contents->GetLastCommittedURL()); - - EXPECT_TRUE(chrome_browser_interstitials::IsShowingHttpsFirstModeInterstitial( - contents)); - - // Verify that navigation event metrics were correctly recorded. - histograms()->ExpectTotalCount(kEventHistogram, 3); - histograms()->ExpectBucketCount(kEventHistogram, Event::kUpgradeAttempted, 1); - histograms()->ExpectBucketCount(kEventHistogram, Event::kUpgradeFailed, 1); - histograms()->ExpectBucketCount(kEventHistogram, Event::kUpgradeCertError, 1); -} - -// If the user triggers an HTTPS-Only Mode interstitial for a host and then -// clicks through the interstitial, they should end up on the HTTP URL. -IN_PROC_BROWSER_TEST_F(HttpsUpgradesBrowserTest, - InterstitialBypassed_HttpFallbackLoaded) { - GURL http_url = http_server()->GetURL("bad-https.test", "/simple.html"); - - auto* contents = browser()->tab_strip_model()->GetActiveWebContents(); - NavigateAndWaitForFallback(contents, http_url); - EXPECT_TRUE(chrome_browser_interstitials::IsShowingHttpsFirstModeInterstitial( - contents)); - - // Proceed through the interstitial, which will add the host to the allowlist - // and navigate to the HTTP fallback URL. - ProceedThroughInterstitial(contents); - EXPECT_EQ(http_url, contents->GetLastCommittedURL()); - - // Verify that navigation event metrics were correctly recorded. - histograms()->ExpectTotalCount(kEventHistogram, 3); - histograms()->ExpectBucketCount(kEventHistogram, Event::kUpgradeAttempted, 1); - histograms()->ExpectBucketCount(kEventHistogram, Event::kUpgradeFailed, 1); - histograms()->ExpectBucketCount(kEventHistogram, Event::kUpgradeCertError, 1); - - // Verify that the interstitial metrics were correctly recorded. - histograms()->ExpectTotalCount("interstitial.https_first_mode.decision", 2); - histograms()->ExpectBucketCount( - "interstitial.https_first_mode.decision", - security_interstitials::MetricsHelper::Decision::SHOW, 1); - histograms()->ExpectBucketCount( - "interstitial.https_first_mode.decision", - security_interstitials::MetricsHelper::Decision::PROCEED, 1); -} - -// If the upgraded HTTPS URL is not available due to a net error, it should -// trigger the HTTPS-Only Mode interstitial and offer fallback. -IN_PROC_BROWSER_TEST_F(HttpsUpgradesBrowserTest, - NetErrorOnUpgrade_ShouldInterstitial) { - GURL http_url = http_server()->GetURL("foo.test", "/close-socket"); - GURL https_url = https_server()->GetURL("foo.test", "/close-socket"); - - auto* contents = browser()->tab_strip_model()->GetActiveWebContents(); - NavigateAndWaitForFallback(contents, http_url); - EXPECT_EQ(http_url, contents->GetLastCommittedURL()); - - EXPECT_TRUE(chrome_browser_interstitials::IsShowingHttpsFirstModeInterstitial( - contents)); - - // Verify that navigation event metrics were correctly recorded. - histograms()->ExpectTotalCount(kEventHistogram, 3); - histograms()->ExpectBucketCount(kEventHistogram, Event::kUpgradeAttempted, 1); - histograms()->ExpectBucketCount(kEventHistogram, Event::kUpgradeFailed, 1); - histograms()->ExpectBucketCount(kEventHistogram, Event::kUpgradeNetError, 1); -} - -// Navigations in subframes should not get upgraded by HTTPS-Only Mode. They -// should be blocked as mixed content. -IN_PROC_BROWSER_TEST_F(HttpsUpgradesBrowserTest, - HttpsParentHttpSubframeNavigation_Blocked) { - const GURL parent_url( - https_server()->GetURL("foo.test", "/iframe_blank.html")); - const GURL iframe_url(http_server()->GetURL("foo.test", "/simple.html")); - - auto* contents = browser()->tab_strip_model()->GetActiveWebContents(); - EXPECT_TRUE(content::NavigateToURL(contents, parent_url)); - - content::TestNavigationObserver nav_observer(contents, 1); - EXPECT_TRUE(content::NavigateIframeToURL(contents, "test", iframe_url)); - nav_observer.Wait(); - EXPECT_NE(iframe_url, nav_observer.last_navigation_url()); - - // Verify that no navigation event metrics were recorded. - histograms()->ExpectTotalCount(kEventHistogram, 0); -} - -// Navigating to an HTTP URL in a subframe of an HTTP page should not upgrade -// the subframe navigation to HTTPS (even if the subframe navigation is to a -// different host than the parent frame). -IN_PROC_BROWSER_TEST_F(HttpsUpgradesBrowserTest, - HttpParentHttpSubframeNavigation_NotUpgraded) { - // The parent frame will fail to upgrade to HTTPS. - const GURL parent_url( - http_server()->GetURL("bad-https.test", "/iframe_blank.html")); - const GURL iframe_url(http_server()->GetURL("bar.test", "/simple.html")); - - // Navigate to `parent_url` and bypass the HTTPS-Only Mode warning. - auto* contents = browser()->tab_strip_model()->GetActiveWebContents(); - NavigateAndWaitForFallback(contents, parent_url); - - EXPECT_TRUE(chrome_browser_interstitials::IsShowingHttpsFirstModeInterstitial( - contents)); - // Proceeding through the interstitial will add the hostname to the allowlist. - ProceedThroughInterstitial(contents); - - // Verify that navigation event metrics were recorded for the main frame. - histograms()->ExpectTotalCount(kEventHistogram, 3); - - // Navigate the iframe to `iframe_url`. It should successfully navigate and - // not get upgraded to HTTPS as the hostname is now in the allowlist. - content::TestNavigationObserver nav_observer(contents, 1); - EXPECT_TRUE(content::NavigateIframeToURL(contents, "test", iframe_url)); - nav_observer.Wait(); - EXPECT_EQ(iframe_url, nav_observer.last_navigation_url()); - - // Verify that no new navigation event metrics were recorded for the subframe. - histograms()->ExpectTotalCount(kEventHistogram, 3); -} - -// Tests that a navigation to the HTTP version of a site with an HTTPS version -// that is slow to respond gets upgraded to HTTPS but times out and shows the -// HTTPS-Only Mode interstitial. -IN_PROC_BROWSER_TEST_F(HttpsUpgradesBrowserTest, SlowHttps_ShouldInterstitial) { - // Set timeout to zero so that HTTPS upgrades immediately timeout. - HttpsOnlyModeNavigationThrottle::set_timeout_for_testing(0); - - const GURL http_url = http_server()->GetURL("foo.test", "/hung"); - auto* contents = browser()->tab_strip_model()->GetActiveWebContents(); - NavigateAndWaitForFallback(contents, http_url); - - EXPECT_TRUE(chrome_browser_interstitials::IsShowingHttpsFirstModeInterstitial( - contents)); - EXPECT_EQ(http_url, contents->GetLastCommittedURL()); -} - -// Tests that an HTTP POST form navigation to "bar.test" from an HTTP page on -// "foo.test" is not upgraded to HTTPS. (HTTP form navigations from HTTPS are -// blocked by the Mixed Forms warning.) -IN_PROC_BROWSER_TEST_F(HttpsUpgradesBrowserTest, HttpPageHttpPost_NotUpgraded) { - // Point the HTTP form target to "bar.test". - base::StringPairs replacement_text; - replacement_text.emplace_back(make_pair( - "REPLACE_WITH_HOST_AND_PORT", - net::HostPortPair::FromURL(http_server()->GetURL("foo.test", "/")) - .ToString())); - auto replacement_path = net::test_server::GetFilePathWithReplacements( - "/ssl/page_with_form_targeting_http_url.html", replacement_text); - - // Navigate to the page hosting the form on "foo.test". The HTTPS-Only Mode - // interstitial should trigger. - auto* contents = browser()->tab_strip_model()->GetActiveWebContents(); - content::NavigateToURLBlockUntilNavigationsComplete( - contents, http_server()->GetURL("bad-https.test", replacement_path), 2); - EXPECT_TRUE(chrome_browser_interstitials::IsShowingHttpsFirstModeInterstitial( - contents)); - - // Proceed through the interstitial to add the hostname to the allowlist. - ProceedThroughInterstitial(contents); - - // Verify that navigation event metrics were recorded for the initial page. - histograms()->ExpectTotalCount(kEventHistogram, 3); - - // Submit the form and wait for the navigation to complete. - content::TestNavigationObserver nav_observer(contents, 1); - ASSERT_TRUE(content::ExecuteScript( - contents, "document.getElementById('submit').click();")); - nav_observer.Wait(); - - // Check that the navigation has ended up on the HTTP target. - EXPECT_EQ("foo.test", contents->GetLastCommittedURL().host()); - EXPECT_TRUE(contents->GetLastCommittedURL().SchemeIs(url::kHttpScheme)); - - // Verify that no new navigation event metrics were recorded for the POST - // navigation. - histograms()->ExpectTotalCount(kEventHistogram, 3); -} - -// Tests that if an HTTPS navigation redirects to HTTP on a different host, it -// should upgrade to HTTPS on that new host. (A downgrade redirect on the same -// host would imply a redirect loop.) -IN_PROC_BROWSER_TEST_F(HttpsUpgradesBrowserTest, - HttpsToHttpRedirect_ShouldUpgrade) { - GURL target_url = http_server()->GetURL("bar.test", "/title1.html"); - GURL url = https_server()->GetURL("foo.test", - "/server-redirect?" + target_url.spec()); - - auto* contents = browser()->tab_strip_model()->GetActiveWebContents(); - - // NavigateToURL() returns `false` because the final redirected URL does not - // match `url`. Separately ensure the navigation succeeded using a navigation - // observer. - content::TestNavigationObserver nav_observer(contents, 1); - EXPECT_FALSE(content::NavigateToURL(contents, url)); - nav_observer.Wait(); - EXPECT_TRUE(nav_observer.last_navigation_succeeded()); - - EXPECT_TRUE(contents->GetLastCommittedURL().SchemeIs(url::kHttpsScheme)); - EXPECT_EQ("bar.test", contents->GetLastCommittedURL().host()); - - // Verify that navigation event metrics were correctly recorded. - histograms()->ExpectTotalCount(kEventHistogram, 2); - histograms()->ExpectBucketCount(kEventHistogram, Event::kUpgradeAttempted, 1); - histograms()->ExpectBucketCount(kEventHistogram, Event::kUpgradeSucceeded, 1); -} - -// Tests that navigating to an HTTPS page that downgrades to HTTP on the same -// host will fail and trigger the HTTPS-Only Mode interstitial (due to the -// redirect loop hitting the redirect limit). -IN_PROC_BROWSER_TEST_F(HttpsUpgradesBrowserTest, - RedirectLoop_ShouldInterstitial) { - // Set up a new test server instance so it can have a custom handler. - net::EmbeddedTestServer downgrading_server{ - net::EmbeddedTestServer::TYPE_HTTPS}; - // Downgrade by swapping the scheme for HTTP. HTTPS-Only Mode will upgrade it - // back to HTTPS. - downgrading_server.RegisterRequestHandler(base::BindLambdaForTesting( - [&](const net::test_server::HttpRequest& request) - -> std::unique_ptr { - GURL::Replacements http_downgrade; - http_downgrade.SetSchemeStr(url::kHttpScheme); - // The HttpRequest will by default refer to the test server by the - // loopback address rather than any hostname in the navigation (i.e., - // the EmbeddedTestServer has no notion of virtual hosts). This - // explicitly sets the hostname back to the test host so that this - // doesn't fail due to the exception for localhost. - http_downgrade.SetHostStr("foo.test"); - auto redirect_url = request.GetURL().ReplaceComponents(http_downgrade); - auto response = std::make_unique(); - response->set_code(net::HTTP_TEMPORARY_REDIRECT); - response->AddCustomHeader("Location", redirect_url.spec()); - return response; - })); - ASSERT_TRUE(downgrading_server.Start()); - HttpsOnlyModeUpgradeInterceptor::SetHttpsPortForTesting( - downgrading_server.port()); - - GURL url = downgrading_server.GetURL("foo.test", "/"); - auto* contents = browser()->tab_strip_model()->GetActiveWebContents(); - NavigateAndWaitForFallback(contents, url); - EXPECT_TRUE(chrome_browser_interstitials::IsShowingHttpsFirstModeInterstitial( - contents)); - - // Verify that navigation event metrics were correctly recorded. - histograms()->ExpectTotalCount(kEventHistogram, 3); - histograms()->ExpectBucketCount(kEventHistogram, Event::kUpgradeAttempted, 1); - histograms()->ExpectBucketCount(kEventHistogram, Event::kUpgradeFailed, 1); - histograms()->ExpectBucketCount(kEventHistogram, Event::kUpgradeNetError, 1); -} - -// Tests that the security level is WARNING when the HTTPS-Only Mode -// interstitial is shown for a net error on HTTPS. (Without HTTPS-Only Mode, a -// net error would be a security level of NONE.) -IN_PROC_BROWSER_TEST_F(HttpsUpgradesBrowserTest, - NetErrorOnUpgrade_SecurityLevelWarning) { - GURL http_url = http_server()->GetURL("foo.test", "/close-socket"); - GURL https_url = https_server()->GetURL("foo.test", "/close-socket"); - - auto* contents = browser()->tab_strip_model()->GetActiveWebContents(); - NavigateAndWaitForFallback(contents, http_url); - EXPECT_EQ(http_url, contents->GetLastCommittedURL()); - - EXPECT_TRUE(chrome_browser_interstitials::IsShowingHttpsFirstModeInterstitial( - contents)); - - auto* helper = SecurityStateTabHelper::FromWebContents(contents); - EXPECT_EQ(security_state::WARNING, helper->GetSecurityLevel()); - - // Proceed through the interstitial to navigate to the HTTP site. The HTTP - // site results in a net error, which should have security level NONE (as no - // connection was made). - ProceedThroughInterstitial(contents); - EXPECT_EQ(security_state::NONE, helper->GetSecurityLevel()); -} - -// Tests that the security level is WARNING when the HTTPS-Only Mode -// interstitial is shown for a cert error on HTTPS. (Without HTTPS-Only Mode, a -// a cert error would be a security level of DANGEROUS.) After clicking through -// the interstitial, the security level should still be WARNING. -IN_PROC_BROWSER_TEST_F(HttpsUpgradesBrowserTest, - BrokenSSLOnUpgrade_SecurityLevelWarning) { - GURL http_url = http_server()->GetURL("bad-https.test", "/simple.html"); - GURL https_url = https_server()->GetURL("bad-https.test", "/simple.html"); - - auto* contents = browser()->tab_strip_model()->GetActiveWebContents(); - NavigateAndWaitForFallback(contents, http_url); - EXPECT_EQ(http_url, contents->GetLastCommittedURL()); - - EXPECT_TRUE(chrome_browser_interstitials::IsShowingHttpsFirstModeInterstitial( - contents)); - - auto* helper = SecurityStateTabHelper::FromWebContents(contents); - EXPECT_EQ(security_state::WARNING, helper->GetSecurityLevel()); - - // Proceed through the interstitial to navigate to the HTTP page. The security - // level should still be WARNING. - ProceedThroughInterstitial(contents); - EXPECT_EQ(security_state::WARNING, helper->GetSecurityLevel()); -} - -// Regression test for crbug.com/1233207. -// Tests the case where the HTTP version of a site redirects to HTTPS, but the -// HTTPS version of the site has a cert error. If the user initially navigates -// to the HTTP URL, then HTTPS-First Mode should upgrade the navigation to HTTPS -// and trigger the HTTPS-First Mode interstitial when that fails, but if the -// user clicks through the HTTPS-First Mode interstitial and falls back into the -// HTTP->HTTPS redirect back to the cert error, then the SSL interstitial should -// be shown and the user should be able to click through the SSL interstitial to -// visit the HTTPS version of the site (but in a DANGEROUS security level -// state). -IN_PROC_BROWSER_TEST_F(HttpsUpgradesBrowserTest, - HttpsUpgradeWithBrokenSSL_ShouldTriggerSSLInterstitial) { - // Set up a new test server instance so it can have a custom handler that - // redirects to the HTTPS server. - net::EmbeddedTestServer upgrading_server{net::EmbeddedTestServer::TYPE_HTTP}; - upgrading_server.RegisterRequestHandler(base::BindLambdaForTesting( - [&](const net::test_server::HttpRequest& request) - -> std::unique_ptr { - auto response = std::make_unique(); - response->set_code(net::HTTP_TEMPORARY_REDIRECT); - response->AddCustomHeader( - "Location", - "https://bad-https.test:" + - base::NumberToString( - HttpsOnlyModeUpgradeInterceptor::GetHttpsPortForTesting()) + - "/simple.html"); - return response; - })); - HttpsOnlyModeUpgradeInterceptor::SetHttpPortForTesting( - upgrading_server.port()); - ASSERT_TRUE(upgrading_server.Start()); - - GURL http_url = upgrading_server.GetURL("bad-https.test", "/simple.html"); - // HTTPS server will have a cert error. - GURL https_url = https_server()->GetURL("bad-https.test", "/simple.html"); - - auto* contents = browser()->tab_strip_model()->GetActiveWebContents(); - NavigateAndWaitForFallback(contents, http_url); - EXPECT_EQ(http_url, contents->GetLastCommittedURL()); - - // The HTTPS-First Mode interstitial should trigger first. - EXPECT_TRUE(chrome_browser_interstitials::IsShowingHttpsFirstModeInterstitial( - contents)); - - // Proceeding through the HTTPS-First Mode interstitial will hit the upgrading - // server's HTTP->HTTPS redirect. This should result in an SSL interstitial - // (not an HTTPS-First Mode interstitial). - ProceedThroughInterstitial(contents); - EXPECT_TRUE(chrome_browser_interstitials::IsShowingSSLInterstitial(contents)); - - // Proceeding through the SSL interstitial should navigate to the HTTPS - // version of the site but with the DANGEROUS security level. - ProceedThroughInterstitial(contents); - EXPECT_EQ(https_url, contents->GetLastCommittedURL()); - auto* helper = SecurityStateTabHelper::FromWebContents(contents); - EXPECT_EQ(security_state::DANGEROUS, helper->GetSecurityLevel()); - - // Verify that navigation event metrics were correctly recorded. They should - // only have been recorded for the initial navigation that resulted in the - // HTTPS-First Mode interstitial. - histograms()->ExpectTotalCount(kEventHistogram, 3); - histograms()->ExpectBucketCount(kEventHistogram, Event::kUpgradeAttempted, 1); - histograms()->ExpectBucketCount(kEventHistogram, Event::kUpgradeFailed, 1); - histograms()->ExpectBucketCount(kEventHistogram, Event::kUpgradeCertError, 1); - - // Verify that the interstitial metrics were correctly recorded. - histograms()->ExpectBucketCount( - "interstitial.https_first_mode.decision", - security_interstitials::MetricsHelper::Decision::SHOW, 1); - histograms()->ExpectBucketCount( - "interstitial.https_first_mode.decision", - security_interstitials::MetricsHelper::Decision::PROCEED, 1); -} - -// Tests that clicking the "Learn More" link in the HTTPS-First Mode -// interstitial opens a new tab for the help center article. -IN_PROC_BROWSER_TEST_F(HttpsUpgradesBrowserTest, InterstitialLearnMoreLink) { - GURL http_url = http_server()->GetURL("foo.test", "/close-socket"); - GURL https_url = https_server()->GetURL("foo.test", "/close-socket"); - - auto* contents = browser()->tab_strip_model()->GetActiveWebContents(); - NavigateAndWaitForFallback(contents, http_url); - EXPECT_EQ(http_url, contents->GetLastCommittedURL()); - - EXPECT_TRUE(chrome_browser_interstitials::IsShowingHttpsFirstModeInterstitial( - contents)); - - // Simulate clicking the learn more link (CMD_OPEN_HELP_CENTER). - ASSERT_TRUE(content::ExecuteScript( - contents, "window.certificateErrorPageController.openHelpCenter();")); - - // New tab should include the p-link "first_mode". - EXPECT_EQ(browser() - ->tab_strip_model() - ->GetActiveWebContents() - ->GetVisibleURL() - .query(), - "p=first_mode"); - - // Verify that the interstitial metrics were correctly recorded. - histograms()->ExpectBucketCount( - "interstitial.https_first_mode.decision", - security_interstitials::MetricsHelper::Decision::SHOW, 1); - histograms()->ExpectBucketCount( - "interstitial.https_first_mode.interaction", - security_interstitials::MetricsHelper::Interaction::TOTAL_VISITS, 1); - histograms()->ExpectBucketCount( - "interstitial.https_first_mode.interaction", - security_interstitials::MetricsHelper::Interaction::SHOW_LEARN_MORE, 1); -} - -// Tests that if the user bypasses the HTTPS-First Mode interstitial, and then -// later the server fixes their HTTPS support and the user successfully connects -// over HTTPS, the allowlist entry is cleared (so HFM will kick in again for -// that site). -IN_PROC_BROWSER_TEST_F(HttpsUpgradesBrowserTest, BadHttpsFollowedByGoodHttps) { - GURL http_url = http_server()->GetURL("foo.test", "/close-socket"); - GURL bad_https_url = https_server()->GetURL("foo.test", "/close-socket"); - GURL good_https_url = https_server()->GetURL("foo.test", "/ssl/google.html"); - - ASSERT_EQ(http_url.host(), bad_https_url.host()); - ASSERT_EQ(bad_https_url.host(), good_https_url.host()); - - auto* tab = browser()->tab_strip_model()->GetActiveWebContents(); - auto* profile = Profile::FromBrowserContext(tab->GetBrowserContext()); - auto* state = static_cast( - profile->GetSSLHostStateDelegate()); - - // First check that main frame requests revoke the decision. - - // Navigate to `http_url`, which will get upgraded to `bad_https_url`. - NavigateAndWaitForFallback(tab, http_url); - - ASSERT_TRUE( - chrome_browser_interstitials::IsShowingHttpsFirstModeInterstitial(tab)); - ProceedThroughInterstitial(tab); - EXPECT_TRUE(state->HasAllowException( - http_url.host(), tab->GetPrimaryMainFrame()->GetStoragePartition())); - - EXPECT_TRUE(content::NavigateToURL(tab, good_https_url)); - EXPECT_FALSE(state->HasAllowException( - http_url.host(), tab->GetPrimaryMainFrame()->GetStoragePartition())); - - // Rarely, an open connection with the bad cert might be reused for the next - // navigation, which is supposed to show an interstitial. Close open - // connections to ensure a fresh connection (and certificate validation) for - // the next navigation. See https://crbug.com/1150592. A deeper fix for this - // issue would be to unify certificate bypass logic which is currently split - // between the net stack and content layer; see https://crbug.com/488043. - // See also: SSLUITest.BadCertFollowedByGoodCert. - state->RevokeUserAllowExceptionsHard(http_url.host()); - - // Now check that subresource requests revoke the decision. - - // Navigate to `http_url`, which will get upgraded to `bad_https_url`. - NavigateAndWaitForFallback(tab, http_url); - - ASSERT_TRUE( - chrome_browser_interstitials::IsShowingHttpsFirstModeInterstitial(tab)); - ProceedThroughInterstitial(tab); - EXPECT_TRUE(state->HasAllowException( - http_url.host(), tab->GetPrimaryMainFrame()->GetStoragePartition())); - - // Load "logo.gif" as an image on the page. - GURL image = https_server()->GetURL("foo.test", "/ssl/google_files/logo.gif"); - bool result = false; - EXPECT_TRUE(ExecuteScriptAndExtractBool( - tab, - std::string("var img = document.createElement('img');img.src ='") + - image.spec() + - "';img.onload=function() { " - "window.domAutomationController.send(true); };" - "document.body.appendChild(img);", - &result)); - EXPECT_TRUE(result); - - EXPECT_FALSE(state->HasAllowException( - http_url.host(), tab->GetPrimaryMainFrame()->GetStoragePartition())); -} - -// Tests that clicking the "Go back" button in the HTTPS-First Mode interstitial -// navigates back to the previous page (about:blank in this case). -IN_PROC_BROWSER_TEST_F(HttpsUpgradesBrowserTest, InterstitialGoBack) { - GURL http_url = http_server()->GetURL("foo.test", "/close-socket"); - GURL https_url = https_server()->GetURL("foo.test", "/close-socket"); - - auto* contents = browser()->tab_strip_model()->GetActiveWebContents(); - NavigateAndWaitForFallback(contents, http_url); - EXPECT_EQ(http_url, contents->GetLastCommittedURL()); - - EXPECT_TRUE(chrome_browser_interstitials::IsShowingHttpsFirstModeInterstitial( - contents)); - - // Simulate clicking the "Go back" button. - DontProceedThroughInterstitial(contents); - - EXPECT_EQ(GURL("about:blank"), contents->GetLastCommittedURL()); - - // Verify that the interstitial metrics were correctly recorded. - histograms()->ExpectBucketCount( - "interstitial.https_first_mode.decision", - security_interstitials::MetricsHelper::Decision::SHOW, 1); - histograms()->ExpectBucketCount( - "interstitial.https_first_mode.decision", - security_interstitials::MetricsHelper::Decision::DONT_PROCEED, 1); -} - -// Tests that closing the tab of the HTTPS-First Mode interstitial counts as -// not proceeding through the interstitial for metrics. -IN_PROC_BROWSER_TEST_F(HttpsUpgradesBrowserTest, CloseInterstitialTab) { - GURL http_url = http_server()->GetURL("foo.test", "/close-socket"); - GURL https_url = https_server()->GetURL("foo.test", "/close-socket"); - - auto* contents = browser()->tab_strip_model()->GetActiveWebContents(); - NavigateAndWaitForFallback(contents, http_url); - EXPECT_EQ(http_url, contents->GetLastCommittedURL()); - - EXPECT_TRUE(chrome_browser_interstitials::IsShowingHttpsFirstModeInterstitial( - contents)); - - // Leave the interstitial by closing the tab. - chrome::CloseWebContents(browser(), contents, false); - - // Verify that the interstitial metrics were correctly recorded. - histograms()->ExpectBucketCount( - "interstitial.https_first_mode.decision", - security_interstitials::MetricsHelper::Decision::SHOW, 1); - histograms()->ExpectBucketCount( - "interstitial.https_first_mode.decision", - security_interstitials::MetricsHelper::Decision::DONT_PROCEED, 1); -} - -// Tests that if a user allowlists a host and then does not visit it again for -// seven days (the expiration period), then the interstitial will be shown again -// the next time they visit the host. -IN_PROC_BROWSER_TEST_F(HttpsUpgradesBrowserTest, AllowlistEntryExpires) { - content::WebContents* contents = - browser()->tab_strip_model()->GetActiveWebContents(); - Profile* profile = Profile::FromBrowserContext(contents->GetBrowserContext()); - content::SSLHostStateDelegate* state = profile->GetSSLHostStateDelegate(); - - // Set a testing clock on the StatefulSSLHostStateDelegate, keeping a pointer - // to the clock object around so the test can manipulate time. `chrome_state` - // takes ownership of `clock`. - auto clock = std::make_unique(); - auto* clock_ptr = clock.get(); - StatefulSSLHostStateDelegate* chrome_state = - static_cast(state); - chrome_state->SetClockForTesting(std::move(clock)); - - // Start the clock at standard system time. - clock_ptr->SetNow(base::Time::NowFromSystemTime()); - - // Visit a host that doesn't support HTTPS for the first time, and click - // through the HTTPS-First Mode interstitial to allowlist the host. - GURL http_url = http_server()->GetURL("bad-https.test", "/simple.html"); - NavigateAndWaitForFallback(contents, http_url); - EXPECT_TRUE(chrome_browser_interstitials::IsShowingHttpsFirstModeInterstitial( - contents)); - ProceedThroughInterstitial(contents); - EXPECT_EQ(http_url, contents->GetLastCommittedURL()); - EXPECT_TRUE(state->IsHttpAllowedForHost( - http_url.host(), contents->GetPrimaryMainFrame()->GetStoragePartition())); - - // Simulate the clock advancing by eight days, which is past the expiration - // point. - clock_ptr->Advance(base::Days(8)); - - // The host should no longer be allowlisted, and the interstitial should - // trigger again. - EXPECT_FALSE(state->IsHttpAllowedForHost( - http_url.host(), contents->GetPrimaryMainFrame()->GetStoragePartition())); - NavigateAndWaitForFallback(contents, http_url); - EXPECT_TRUE(chrome_browser_interstitials::IsShowingHttpsFirstModeInterstitial( - contents)); -} - -// Tests that re-visiting an allowlisted host bumps the expiration time to a new -// seven days in the future from now. -IN_PROC_BROWSER_TEST_F(HttpsUpgradesBrowserTest, RevisitingBumpsExpiration) { - content::WebContents* contents = - browser()->tab_strip_model()->GetActiveWebContents(); - Profile* profile = Profile::FromBrowserContext(contents->GetBrowserContext()); - content::SSLHostStateDelegate* state = profile->GetSSLHostStateDelegate(); - - // Set a testing clock on the StatefulSSLHostStateDelegate, keeping a pointer - // to the clock object around so the test can manipulate time. `chrome_state` - // takes ownership of `clock`. - auto clock = std::make_unique(); - auto* clock_ptr = clock.get(); - StatefulSSLHostStateDelegate* chrome_state = - static_cast(state); - chrome_state->SetClockForTesting(std::move(clock)); - - // Start the clock at standard system time. - clock_ptr->SetNow(base::Time::NowFromSystemTime()); - - // Visit a host that doesn't support HTTPS for the first time, and click - // through the HTTPS-First Mode interstitial to allowlist the host. - GURL http_url = http_server()->GetURL("bad-https.test", "/simple.html"); - NavigateAndWaitForFallback(contents, http_url); - EXPECT_TRUE(chrome_browser_interstitials::IsShowingHttpsFirstModeInterstitial( - contents)); - ProceedThroughInterstitial(contents); - EXPECT_EQ(http_url, contents->GetLastCommittedURL()); - EXPECT_TRUE(state->IsHttpAllowedForHost( - http_url.host(), contents->GetPrimaryMainFrame()->GetStoragePartition())); - - // Simulate the clock advancing by five days. - clock_ptr->Advance(base::Days(5)); - - // Navigate to the host again; this will reset the allowlist expiration to - // now + 7 days. - EXPECT_TRUE(content::NavigateToURL(contents, http_url)); - - // Simulate the clock advancing another five days. This will be _after_ the - // initial expiration date of the allowlist entry, but _before_ the bumped - // expiration date from the second navigation. - clock_ptr->Advance(base::Days(5)); - EXPECT_TRUE(state->IsHttpAllowedForHost( - http_url.host(), contents->GetPrimaryMainFrame()->GetStoragePartition())); - EXPECT_TRUE(content::NavigateToURL(contents, http_url)); - EXPECT_FALSE( - chrome_browser_interstitials::IsShowingHttpsFirstModeInterstitial( - contents)); -} - -// Tests that if a hostname has an HSTS entry registered, then HTTPS-First Mode -// should not try to upgrade it (instead allowing HSTS to handle the upgrade as -// it is more strict). -IN_PROC_BROWSER_TEST_F(HttpsUpgradesBrowserTest, PreferHstsOverHttpsFirstMode) { - content::WebContents* contents = - browser()->tab_strip_model()->GetActiveWebContents(); - Profile* profile = Profile::FromBrowserContext(contents->GetBrowserContext()); - - // URL for HTTPS server that will result in a certificate error. - GURL https_url = https_server()->GetURL("bad-https.test", "/simple.html"); - - // HTTP version of that URL that will get upgraded to HTTPS (but with the - // correct port for the HTTPS server -- the test code can configure - // HTTPS-First Mode to be aware of the different ports, but can't do that for - // HSTS). - GURL::Replacements downgrade_scheme_to_http; - downgrade_scheme_to_http.SetSchemeStr(url::kHttpScheme); - GURL http_url = https_url.ReplaceComponents(downgrade_scheme_to_http); - - // Add hostname to the TransportSecurityState. - base::Time expiry = base::Time::Now() + base::Days(100); - bool include_subdomains = false; - auto* network_context = - profile->GetDefaultStoragePartition()->GetNetworkContext(); - base::RunLoop run_loop; - network_context->AddHSTS(http_url.host(), expiry, include_subdomains, - run_loop.QuitClosure()); - run_loop.Run(); - - // Navigate to the HTTP URL. It should get upgraded to HTTPS and trigger a - // fatal certificate error (because of HTTPS) instead of falling back to the - // HTTPS-First Mode interstitial. - EXPECT_FALSE(content::NavigateToURL(contents, http_url)); - EXPECT_FALSE( - chrome_browser_interstitials::IsShowingHttpsFirstModeInterstitial( - contents)); - EXPECT_TRUE(chrome_browser_interstitials::IsShowingSSLInterstitial(contents)); - - // Verify that no HFM event histograms were emitted (to check that HFM did not - // trigger for this navigation at all). - histograms()->ExpectTotalCount(kEventHistogram, 0); -} - -// Regression test for crbug.com/1272781. Previously, performing back/forward -// navigations around the HTTPS-First Mode interstitial could cause history -// entries to dropped. -// TODO(crbug.com/1394910): Going back from HFM interstitial to an HTTPS page -// still loses the security indicator of the previous HTTPS page. -IN_PROC_BROWSER_TEST_F(HttpsUpgradesBrowserTest, - InterstitialFallbackMaintainsHistory) { - GURL good_https_url = - https_server()->GetURL("site1.test", "/defaultresponse"); - - // Set up a new test server instance so it can have a custom handler. - net::EmbeddedTestServer downgrading_server{ - net::EmbeddedTestServer::TYPE_HTTPS}; - // Downgrade by swapping the scheme for HTTP. HTTPS-First Mode will upgrade it - // back to HTTPS. - downgrading_server.RegisterRequestHandler(base::BindLambdaForTesting( - [&](const net::test_server::HttpRequest& request) - -> std::unique_ptr { - GURL::Replacements http_downgrade; - http_downgrade.SetSchemeStr(url::kHttpScheme); - // The HttpRequest will by default refer to the test server by the - // loopback address rather than any hostname in the navigation (i.e., - // the EmbeddedTestServer has no notion of virtual hosts). This - // explicitly sets the hostname back to the test host so that this - // doesn't fail due to the exception for localhost. - http_downgrade.SetHostStr("site2.test"); - auto redirect_url = request.GetURL().ReplaceComponents(http_downgrade); - auto response = std::make_unique(); - response->set_code(net::HTTP_TEMPORARY_REDIRECT); - response->AddCustomHeader("Location", redirect_url.spec()); - return response; - })); - ASSERT_TRUE(downgrading_server.Start()); - HttpsOnlyModeUpgradeInterceptor::SetHttpsPortForTesting( - downgrading_server.port()); - - GURL downgrading_https_url = downgrading_server.GetURL("site2.test", "/"); - GURL::Replacements swap_http_scheme; - swap_http_scheme.SetSchemeStr(url::kHttpScheme); - GURL downgrading_http_url = - downgrading_https_url.ReplaceComponents(swap_http_scheme); - - auto* contents = browser()->tab_strip_model()->GetActiveWebContents(); - - // Navigate to a "good" HTTPS site. - EXPECT_TRUE(content::NavigateToURL(contents, good_https_url)); - - // Navigate to the HTTP version of `downgrading_https_url`, which will get - // upgraded to HTTPS and fail, triggering the HTTPS-First Mode - // interstitial. - content::NavigateToURLBlockUntilNavigationsComplete(contents, - downgrading_http_url, 2); - EXPECT_EQ(downgrading_http_url, contents->GetLastCommittedURL()); - EXPECT_TRUE(chrome_browser_interstitials::IsShowingHttpsFirstModeInterstitial( - contents)); - - // Simulate clicking the browser "back" button. - // TODO(crbug.com/1394910): The incorrect WARNING security state is retained - // from the interstitial page. - EXPECT_TRUE(content::HistoryGoBack(contents)); - EXPECT_EQ(good_https_url, contents->GetLastCommittedURL()); - auto* helper = SecurityStateTabHelper::FromWebContents(contents); - EXPECT_EQ(security_state::WARNING, helper->GetSecurityLevel()); - - // Simulate clicking the browser "forward" button. (The HistoryGoForward() - // call returns `false` because it is an error page.) - EXPECT_FALSE(content::HistoryGoForward(contents)); - EXPECT_EQ(downgrading_http_url, contents->GetLastCommittedURL()); - EXPECT_TRUE(chrome_browser_interstitials::IsShowingHttpsFirstModeInterstitial( - contents)); - - // No forward entry should be present. - EXPECT_FALSE(contents->GetController().CanGoForward()); - - // Simulate clicking the browser "back" button again. Previously this would - // result in `about:blank` being shown. - EXPECT_TRUE(content::HistoryGoBack(contents)); - EXPECT_EQ(good_https_url, contents->GetLastCommittedURL()); - - // Repeat forward one last time. (Previously the user would no longer be able - // to go back any more as the history entries were lost.) - // The HistoryGoForward() call returns `false` because it is an error page. - EXPECT_FALSE(content::HistoryGoForward(contents)); - EXPECT_EQ(downgrading_http_url, contents->GetLastCommittedURL()); - EXPECT_TRUE(chrome_browser_interstitials::IsShowingHttpsFirstModeInterstitial( - contents)); - EXPECT_TRUE(contents->GetController().CanGoBack()); -} - -// A simple test fixture that ensures the kHttpsFirstModeV2 feature is enabled -// and constructs a HistogramTester (so that it gets initialized before browser -// startup). Used for testing pref tracking logic. -class HttpsUpgradesPrefsBrowserTest : public InProcessBrowserTest { - public: - HttpsUpgradesPrefsBrowserTest() = default; - ~HttpsUpgradesPrefsBrowserTest() override = default; - - void SetUp() override { - feature_list_.InitAndEnableFeature(features::kHttpsFirstModeV2); - InProcessBrowserTest::SetUp(); - } - - protected: - void SetPref(bool enabled) { - auto* prefs = browser()->profile()->GetPrefs(); - prefs->SetBoolean(prefs::kHttpsOnlyModeEnabled, enabled); - } - - bool GetPref() const { - auto* prefs = browser()->profile()->GetPrefs(); - return prefs->GetBoolean(prefs::kHttpsOnlyModeEnabled); - } - - base::HistogramTester* histograms() { return &histograms_; } - - private: - base::test::ScopedFeatureList feature_list_; - base::HistogramTester histograms_; -}; - -// Tests that the HTTPS-First Mode pref is recorded at startup and when changed. -// This test requires restarting the browser to test the "at startup" metric in -// order for the preference state to be set up before the HttpsFirstModeService -// is created. -IN_PROC_BROWSER_TEST_F(HttpsUpgradesPrefsBrowserTest, PRE_PrefStatesRecorded) { - // The default pref state is `false`, which should get recorded when the - // initial browser instance is started here. - histograms()->ExpectUniqueSample( - "Security.HttpsFirstMode.SettingEnabledAtStartup", false, 1); - - EXPECT_TRUE(variations::IsInSyntheticTrialGroup("HttpsFirstModeClientSetting", - "Disabled")); - - // Change the pref to true. This should get recorded in the histogram. - SetPref(true); - histograms()->ExpectUniqueSample("Security.HttpsFirstMode.SettingChanged", - true, 1); - EXPECT_TRUE(variations::IsInSyntheticTrialGroup("HttpsFirstModeClientSetting", - "Enabled")); -} - -IN_PROC_BROWSER_TEST_F(HttpsUpgradesPrefsBrowserTest, PrefStatesRecorded) { - // Restarting the browser from the PRE_ test should record the startup pref - // histogram. Checking the unique count also ensures that other profile types - // (e.g. the ChromeOS sign-in profile) don't cause double-counting. - EXPECT_TRUE(GetPref()); - histograms()->ExpectUniqueSample( - "Security.HttpsFirstMode.SettingEnabledAtStartup", true, 1); - EXPECT_TRUE(variations::IsInSyntheticTrialGroup("HttpsFirstModeClientSetting", - "Enabled")); - - // Open an Incognito window. Startup metrics should not get recorded. - CreateIncognitoBrowser(); - histograms()->ExpectTotalCount( - "Security.HttpsFirstMode.SettingEnabledAtStartup", 1); -} diff --git a/chrome/browser/ssl/https_upgrades_interceptor.cc b/chrome/browser/ssl/https_upgrades_interceptor.cc deleted file mode 100644 index 955bb9df9a801..0000000000000 --- a/chrome/browser/ssl/https_upgrades_interceptor.cc +++ /dev/null @@ -1,223 +0,0 @@ -// Copyright 2022 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "chrome/browser/ssl/https_upgrades_interceptor.h" - -#include "base/bind.h" -#include "chrome/browser/browser_process.h" -#include "chrome/browser/profiles/profile.h" -#include "chrome/browser/profiles/profile_manager.h" -#include "chrome/browser/ssl/https_only_mode_tab_helper.h" -#include "chrome/browser/ssl/https_only_mode_upgrade_url_loader.h" -#include "chrome/browser/ssl/stateful_ssl_host_state_delegate_factory.h" -#include "chrome/common/pref_names.h" -#include "components/prefs/pref_service.h" -#include "components/security_interstitials/content/stateful_ssl_host_state_delegate.h" -#include "content/public/browser/storage_partition.h" -#include "content/public/browser/web_contents.h" -#include "extensions/buildflags/buildflags.h" -#include "mojo/public/cpp/bindings/callback_helpers.h" -#include "net/base/url_util.h" -#include "services/network/public/mojom/network_context.mojom.h" -#include "third_party/blink/public/mojom/loader/resource_load_info.mojom.h" -#include "url/gurl.h" -#include "url/url_constants.h" -#include "url/url_util.h" - -#if BUILDFLAG(ENABLE_EXTENSIONS) -#include "components/guest_view/browser/guest_view_base.h" -#endif // BUILDFLAG(ENABLE_EXTENSIONS) - -namespace { - -// Used to handle upgrading/fallback for tests using EmbeddedTestServer which -// uses random ports. -int g_https_port_for_testing = 0; -int g_http_port_for_testing = 0; - -// Only serve upgrade redirects for main frame, GET requests to HTTP URLs. This -// excludes "localhost" (and loopback addresses) as they do not expose traffic -// over the network. -bool ShouldCreateLoader(const network::ResourceRequest& resource_request, - HttpsOnlyModeTabHelper* tab_helper) { - if (resource_request.is_outermost_main_frame && - resource_request.method == "GET" && - !net::IsLocalhost(resource_request.url) && - resource_request.url.SchemeIs(url::kHttpScheme) && - !tab_helper->is_navigation_fallback()) { - return true; - } - return false; -} - -} // namespace - -HttpsUpgradesInterceptor::HttpsUpgradesInterceptor(int frame_tree_node_id) - : frame_tree_node_id_(frame_tree_node_id) {} - -HttpsUpgradesInterceptor::~HttpsUpgradesInterceptor() = default; - -void HttpsUpgradesInterceptor::MaybeCreateLoader( - const network::ResourceRequest& tentative_resource_request, - content::BrowserContext* browser_context, - content::URLLoaderRequestInterceptor::LoaderCallback callback) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - - // If there isn't a BrowserContext/Profile for this, then just allow it. - Profile* profile = Profile::FromBrowserContext(browser_context); - if (!profile || - !g_browser_process->profile_manager()->IsValidProfile(profile)) { - std::move(callback).Run({}); - return; - } - - // Don't upgrade if the HTTPS-First Mode setting isn't enabled. - auto* prefs = profile->GetPrefs(); - if (!prefs || !prefs->GetBoolean(prefs::kHttpsOnlyModeEnabled)) { - std::move(callback).Run({}); - return; - } - - auto* web_contents = - content::WebContents::FromFrameTreeNodeId(frame_tree_node_id_); - // Could be null if the FrameTreeNode's RenderFrameHost is shutting down. - if (!web_contents) { - std::move(callback).Run({}); - return; - } - -#if BUILDFLAG(ENABLE_EXTENSIONS) - // If this is a GuestView (e.g., Chrome Apps ) then HTTPS-First Mode - // should not apply. See crbug.com/1233889 for more details. - if (guest_view::GuestViewBase::IsGuest(web_contents)) { - std::move(callback).Run({}); - return; - } -#endif // BUILDFLAG(ENABLE_EXTENSIONS) - - auto* tab_helper = HttpsOnlyModeTabHelper::FromWebContents(web_contents); - if (!tab_helper) { - HttpsOnlyModeTabHelper::CreateForWebContents(web_contents); - tab_helper = HttpsOnlyModeTabHelper::FromWebContents(web_contents); - } - - // Don't upgrade navigation if it is allowlisted. - StatefulSSLHostStateDelegate* state = - static_cast( - profile->GetSSLHostStateDelegate()); - // StatefulSSLHostStateDelegate can be null during tests. - auto* storage_partition = - web_contents->GetPrimaryMainFrame()->GetStoragePartition(); - if (state && state->IsHttpAllowedForHost( - tentative_resource_request.url.host(), storage_partition)) { - // Renew the allowlist expiration for this host as the user is still - // actively using it. This means that the allowlist entry will stay - // valid until the user stops visiting this host for the entire - // expiration period (one week). - state->AllowHttpForHost(tentative_resource_request.url.host(), - storage_partition); - - std::move(callback).Run({}); - return; - } - - if (!ShouldCreateLoader(tentative_resource_request, tab_helper)) { - std::move(callback).Run({}); - return; - } - - // Check whether this host would be upgraded to HTTPS by HSTS. This requires a - // Mojo call to the network service, so set up a callback to continue the rest - // of the MaybeCreateLoader() logic (passing along the necessary state). The - // HSTS status will be passed as a boolean to - // MaybeCreateLoaderOnHstsQueryCompleted(). If the Mojo call fails, this will - // default to passing `false` and continuing as though the host does not have - // HSTS (i.e., it will proceed with the HTTPS-First Mode logic). - auto query_complete_callback = base::BindOnce( - &HttpsUpgradesInterceptor::MaybeCreateLoaderOnHstsQueryCompleted, - weak_factory_.GetWeakPtr(), tentative_resource_request, - std::move(callback), tab_helper); - network::mojom::NetworkContext* network_context = - profile->GetDefaultStoragePartition()->GetNetworkContext(); - network_context->IsHSTSActiveForHost( - tentative_resource_request.url.host(), - mojo::WrapCallbackWithDefaultInvokeIfNotRun( - std::move(query_complete_callback), - /*is_hsts_active_for_host=*/false)); -} - -void HttpsUpgradesInterceptor::MaybeCreateLoaderOnHstsQueryCompleted( - const network::ResourceRequest& tentative_resource_request, - content::URLLoaderRequestInterceptor::LoaderCallback callback, - HttpsOnlyModeTabHelper* tab_helper, - bool is_hsts_active_for_host) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - - // Don't upgrade this request if HSTS is active for this host. - if (is_hsts_active_for_host) { - std::move(callback).Run({}); - return; - } - - // Mark navigation as upgraded. - tab_helper->set_is_navigation_upgraded(true); - tab_helper->set_fallback_url(tentative_resource_request.url); - CreateHttpsRedirectLoader(tentative_resource_request, std::move(callback)); - // `redirect_url_loader_` can be null after this call. - redirect_url_loader_->StartRedirectToHttps(frame_tree_node_id_); -} - -// static -void HttpsUpgradesInterceptor::SetHttpsPortForTesting(int port) { - g_https_port_for_testing = port; -} - -// static -void HttpsUpgradesInterceptor::SetHttpPortForTesting(int port) { - g_http_port_for_testing = port; -} - -// static -int HttpsUpgradesInterceptor::GetHttpsPortForTesting() { - return g_https_port_for_testing; -} - -// static -int HttpsUpgradesInterceptor::GetHttpPortForTesting() { - return g_http_port_for_testing; -} - -// Creates a redirect URL loader that immediately serves a redirect to the -// upgraded HTTPS version of the URL. -void HttpsUpgradesInterceptor::CreateHttpsRedirectLoader( - const network::ResourceRequest& tentative_resource_request, - content::URLLoaderRequestInterceptor::LoaderCallback callback) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - - redirect_url_loader_ = std::make_unique( - tentative_resource_request, - base::BindOnce(&HttpsUpgradesInterceptor::HandleRedirectLoader, - base::Unretained(this), std::move(callback))); -} - -// Runs `callback` with `handler`. -void HttpsUpgradesInterceptor::HandleRedirectLoader( - content::URLLoaderRequestInterceptor::LoaderCallback callback, - RequestHandler handler) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - - // Handle any failure by using default loader. - if (handler.is_null()) { - redirect_url_loader_.reset(); - // PROCEED. - std::move(callback).Run({}); - return; - } - - // `redirect_url_loader_` now manages its own lifetime via a mojo channel. - // `handler` is guaranteed to be called. It will complete by serving the - // artificial redirect. - redirect_url_loader_.release(); - std::move(callback).Run(std::move(handler)); -} diff --git a/chrome/browser/ssl/https_upgrades_interceptor.h b/chrome/browser/ssl/https_upgrades_interceptor.h deleted file mode 100644 index 9c0e7cf0ab774..0000000000000 --- a/chrome/browser/ssl/https_upgrades_interceptor.h +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright 2022 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CHROME_BROWSER_SSL_HTTPS_UPGRADES_INTERCEPTOR_H_ -#define CHROME_BROWSER_SSL_HTTPS_UPGRADES_INTERCEPTOR_H_ - -#include - -#include "base/sequence_checker.h" -#include "chrome/browser/ssl/https_only_mode_tab_helper.h" -#include "chrome/browser/ssl/https_only_mode_upgrade_url_loader.h" -#include "content/public/browser/url_loader_request_interceptor.h" -#include "services/network/public/cpp/resource_request.h" - -namespace content { -class BrowserContext; -class WebContents; -} // namespace content - -// A class that attempts to intercept HTTP navigation requests and redirect them -// to HTTPS. Its lifetime matches that of the content/ navigation loader code. -class HttpsUpgradesInterceptor : public content::URLLoaderRequestInterceptor { - public: - explicit HttpsUpgradesInterceptor(int frame_tree_node_id); - ~HttpsUpgradesInterceptor() override; - - HttpsUpgradesInterceptor(const HttpsUpgradesInterceptor&) = delete; - HttpsUpgradesInterceptor& operator=(const HttpsUpgradesInterceptor&) = delete; - - // content::URLLoaderRequestInterceptor: - void MaybeCreateLoader( - const network::ResourceRequest& tentative_resource_request, - content::BrowserContext* browser_context, - content::URLLoaderRequestInterceptor::LoaderCallback callback) override; - - // Continuation of MaybeCreateLoader() after querying the network service for - // the HSTS status for the hostname in the request. - void MaybeCreateLoaderOnHstsQueryCompleted( - const network::ResourceRequest& tentative_resource_request, - content::URLLoaderRequestInterceptor::LoaderCallback callback, - HttpsOnlyModeTabHelper* tab_helper, - bool is_hsts_active_for_host); - - // Sets the ports used by the EmbeddedTestServer (which uses random ports) - // to determine the correct port to upgrade/fallback to in tests. - static void SetHttpsPortForTesting(int port); - static void SetHttpPortForTesting(int port); - static int GetHttpsPortForTesting(); - static int GetHttpPortForTesting(); - - private: - // Creates a URL loader that immediately serves a redirect to the HTTPS - // version of the URL. - void CreateHttpsRedirectLoader( - const network::ResourceRequest& tentative_resource_request, - content::URLLoaderRequestInterceptor::LoaderCallback callback); - - // Runs `callback` with `handler`. - void HandleRedirectLoader( - content::URLLoaderRequestInterceptor::LoaderCallback callback, - RequestHandler handler); - - // URLLoader that serves redirects. - std::unique_ptr redirect_url_loader_; - - // Used to access the WebContents for the navigation. - int frame_tree_node_id_; - - SEQUENCE_CHECKER(sequence_checker_); - - base::WeakPtrFactory weak_factory_{this}; -}; - -#endif // CHROME_BROWSER_SSL_HTTPS_ONLY_MODE_UPGRADE_INTERCEPTOR_H_ diff --git a/chrome/browser/ssl/https_upgrades_navigation_throttle.cc b/chrome/browser/ssl/https_upgrades_navigation_throttle.cc deleted file mode 100644 index a425532ecd7e5..0000000000000 --- a/chrome/browser/ssl/https_upgrades_navigation_throttle.cc +++ /dev/null @@ -1,223 +0,0 @@ -// Copyright 2022 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "chrome/browser/ssl/https_upgrades_navigation_throttle.h" - -#include "base/feature_list.h" -#include "base/metrics/histogram_functions.h" -#include "base/time/time.h" -#include "chrome/browser/ssl/https_only_mode_tab_helper.h" -#include "chrome/browser/ssl/https_upgrades_navigation_throttle.h" -#include "chrome/common/chrome_features.h" -#include "chrome/common/pref_names.h" -#include "components/prefs/pref_service.h" -#include "components/security_interstitials/content/security_interstitial_tab_helper.h" -#include "components/security_interstitials/core/https_only_mode_metrics.h" -#include "content/public/browser/browser_thread.h" -#include "content/public/browser/navigation_handle.h" -#include "content/public/browser/navigation_throttle.h" -#include "content/public/browser/web_contents.h" -#include "net/base/net_errors.h" - -using security_interstitials::https_only_mode::Event; - -namespace { - -// Time that the throttle will wait before canceling the upgraded navigation and -// showing the HTTPS-First Mode interstitial. -base::TimeDelta g_fallback_delay = base::Seconds(3); - -// Helper to record an HTTPS-First Mode navigation event. -void RecordHttpsFirstModeNavigation( - security_interstitials::https_only_mode::Event event) { - base::UmaHistogramEnumeration( - security_interstitials::https_only_mode::kEventHistogram, event); -} - -} // namespace - -// static -std::unique_ptr -HttpsUpgradesNavigationThrottle::MaybeCreateThrottleFor( - content::NavigationHandle* handle, - std::unique_ptr blocking_page_factory, - PrefService* prefs) { - DCHECK_CURRENTLY_ON(content::BrowserThread::UI); - - // HTTPS-First Mode is only relevant for primary main-frame HTTP(S) - // navigations. - if (!handle->GetURL().SchemeIsHTTPOrHTTPS() || - !handle->IsInPrimaryMainFrame() || handle->IsSameDocument()) { - return nullptr; - } - - if (!base::FeatureList::IsEnabled(features::kHttpsOnlyMode) || !prefs || - !prefs->GetBoolean(prefs::kHttpsOnlyModeEnabled)) { - return nullptr; - } - - // Ensure that the HttpsOnlyModeTabHelper has been created (this does nothing - // if it has already been created for the WebContents). There are cases where - // the tab helper won't get created by the initialization in - // chrome/browser/ui/tab_helpers.cc but the criteria for adding the throttle - // are still met (see crbug.com/1233889 for one example). - HttpsOnlyModeTabHelper::CreateForWebContents(handle->GetWebContents()); - - return std::make_unique( - handle, std::move(blocking_page_factory)); -} - -HttpsUpgradesNavigationThrottle::HttpsUpgradesNavigationThrottle( - content::NavigationHandle* handle, - std::unique_ptr blocking_page_factory) - : content::NavigationThrottle(handle), - blocking_page_factory_(std::move(blocking_page_factory)) {} - -HttpsUpgradesNavigationThrottle::~HttpsUpgradesNavigationThrottle() = default; - -content::NavigationThrottle::ThrottleCheckResult -HttpsUpgradesNavigationThrottle::WillStartRequest() { - // If the navigation is fallback to HTTP, trigger the interstitial. - auto* handle = navigation_handle(); - auto* contents = handle->GetWebContents(); - auto* tab_helper = HttpsOnlyModeTabHelper::FromWebContents(contents); - if (tab_helper->is_navigation_fallback() && - !handle->GetURL().SchemeIsCryptographic()) { - std::unique_ptr - blocking_page = blocking_page_factory_->CreateHttpsOnlyModeBlockingPage( - contents, handle->GetURL()); - std::string interstitial_html = blocking_page->GetHTMLContents(); - security_interstitials::SecurityInterstitialTabHelper:: - AssociateBlockingPage(handle, std::move(blocking_page)); - return content::NavigationThrottle::ThrottleCheckResult( - content::NavigationThrottle::CANCEL, net::ERR_BLOCKED_BY_CLIENT, - interstitial_html); - } - - // Navigation is HTTPS or an initial HTTP navigation (which will get - // upgraded by the interceptor). - return content::NavigationThrottle::ThrottleAction::PROCEED; -} - -// Called if there is a non-OK net::Error in the completion status. -content::NavigationThrottle::ThrottleCheckResult -HttpsUpgradesNavigationThrottle::WillFailRequest() { - auto* handle = navigation_handle(); - - // If there was no certificate error, SSLInfo will be empty. - const net::SSLInfo info = handle->GetSSLInfo().value_or(net::SSLInfo()); - int cert_status = info.cert_status; - if (!net::IsCertStatusError(cert_status) && - handle->GetNetErrorCode() == net::OK) { - // Don't fallback. - return content::NavigationThrottle::PROCEED; - } - - // Only show the interstitial if the Interceptor attempted to upgrade the - // navigation. - auto* contents = handle->GetWebContents(); - auto* tab_helper = HttpsOnlyModeTabHelper::FromWebContents(contents); - if (tab_helper->is_navigation_upgraded()) { - // Record failure type metrics for upgraded navigations. - RecordHttpsFirstModeNavigation(Event::kUpgradeFailed); - if (net::IsCertificateError(handle->GetNetErrorCode())) { - RecordHttpsFirstModeNavigation(Event::kUpgradeCertError); - } else if (handle->GetNetErrorCode() == net::ERR_TIMED_OUT) { - RecordHttpsFirstModeNavigation(Event::kUpgradeTimedOut); - } else { - RecordHttpsFirstModeNavigation(Event::kUpgradeNetError); - } - - // Mark the navigation as fallback and trigger a new navigation to the - // fallback URL. - tab_helper->set_is_navigation_fallback(true); - - // Copy the original navigation's params to the extent possible but update - // the URL to navigate to the fallback HTTP URL. - content::OpenURLParams params = - content::OpenURLParams::FromNavigationHandle(handle); - params.url = tab_helper->fallback_url(); - // Post a task to navigate to the fallback URL. We don't navigate - // synchronously here, as starting a navigation within a navigation is an - // antipattern. - base::SequencedTaskRunner::GetCurrentDefault()->PostTask( - FROM_HERE, base::BindOnce( - [](base::WeakPtr web_contents, - const content::OpenURLParams& url_params) { - if (!web_contents) { - return; - } - web_contents->OpenURL(url_params); - }, - contents->GetWeakPtr(), std::move(params))); - return content::NavigationThrottle::CANCEL_AND_IGNORE; - } - - return content::NavigationThrottle::PROCEED; -} - -content::NavigationThrottle::ThrottleCheckResult -HttpsUpgradesNavigationThrottle::WillRedirectRequest() { - // If the navigation was upgraded by the Interceptor, then the Throttle's - // WillRedirectRequest() will get triggered by the artificial redirect to - // HTTPS. The HTTPS upgrade will always happen after the Throttle's - // WillStartRequest() (which only checks for fallback HTTP), so tracking - // upgraded requests is deferred to WillRedirectRequest() here. Which - // navigations to upgrade is determined by the Interceptor, not the Throttle. - // - // The navigation may get upgraded at various points during redirects: - // 1. The Interceptor serves an artificial redirect to HTTPS if the - // navigation is upgraded. This means the Throttle will see the upgraded - // navigation state for the first time here in WillRedirectRequest(). - // 2. HTTPS->HTTP downgrades can occur later in the lifecycle of a - // navigation, and will also result in the Interceptor serving an - // artificial redirect to upgrade the navigation. - // - // HTTPS->HTTP downgrades may result in net::ERR_TOO_MANY_REDIRECTS, but these - // redirect loops should hit the cache and not cost too much. If they go too - // long, the fallback timer will kick in. ERR_TOO_MANY_REDIRECTS should result - // in the request failing and triggering fallback. Alternately, the - // Interceptor could log URLs seen and bail if it encounters a redirect loop, - // but it is simpler to rely on existing handling unless the optimization is - // needed. - auto* tab_helper = HttpsOnlyModeTabHelper::FromWebContents( - navigation_handle()->GetWebContents()); - if (tab_helper->is_navigation_upgraded()) { - // Check if the timer is already started, as there may be additional - // redirects on the navigation after the artificial upgrade redirect. - bool timer_started = - navigation_handle()->SetNavigationTimeout(g_fallback_delay); - if (timer_started) { - RecordHttpsFirstModeNavigation(Event::kUpgradeAttempted); - } - } - - return content::NavigationThrottle::PROCEED; -} - -content::NavigationThrottle::ThrottleCheckResult -HttpsUpgradesNavigationThrottle::WillProcessResponse() { - // Clear the status for this navigation as it will successfully commit. - auto* tab_helper = HttpsOnlyModeTabHelper::FromWebContents( - navigation_handle()->GetWebContents()); - if (tab_helper->is_navigation_upgraded()) { - RecordHttpsFirstModeNavigation(Event::kUpgradeSucceeded); - tab_helper->set_is_navigation_upgraded(false); - } - - // Clear the fallback flag, if set. - tab_helper->set_is_navigation_fallback(false); - - return content::NavigationThrottle::PROCEED; -} - -const char* HttpsUpgradesNavigationThrottle::GetNameForLogging() { - return "HttpsUpgradesNavigationThrottle"; -} - -// static -void HttpsUpgradesNavigationThrottle::set_timeout_for_testing( - int timeout_in_seconds) { - g_fallback_delay = base::Seconds(timeout_in_seconds); -} diff --git a/chrome/browser/ssl/https_upgrades_navigation_throttle.h b/chrome/browser/ssl/https_upgrades_navigation_throttle.h deleted file mode 100644 index e31f9f649daf9..0000000000000 --- a/chrome/browser/ssl/https_upgrades_navigation_throttle.h +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright 2022 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CHROME_BROWSER_SSL_HTTPS_UPGRADES_NAVIGATION_THROTTLE_H_ -#define CHROME_BROWSER_SSL_HTTPS_UPGRADES_NAVIGATION_THROTTLE_H_ - -#include - -#include "components/security_interstitials/content/security_blocking_page_factory.h" -#include "content/public/browser/navigation_throttle.h" - -class PrefService; - -// HttpsUpgradesNavigationThrottle is responsible for observing HTTPS-First Mode -// navigations that have been upgraded by HttpsUpgradesInterceptor, timing them -// out if they take too long, and handling failure by triggering fallback -// navigations to HTTP and triggering the HTTPS-First Mode interstitial. -// -// Metadata about the navigation state (as it pertains to HTTPS-First Mode) -// shared between HttpsUpgradesInterceptor and HttpsUpgradesNavigationThrottle -// is stored in an HttpsOnlyModeTabHelper set as user-data on the WebContents in -// which the navigation occurs. (Such metadata might ordinarily be added to -// ChromeNavigationUIData, but the Interceptor only receives a clone of the -// data, so it can't be used as a channel between these classes.) -class HttpsUpgradesNavigationThrottle : public content::NavigationThrottle { - public: - static std::unique_ptr - MaybeCreateThrottleFor( - content::NavigationHandle* handle, - std::unique_ptr blocking_page_factory, - PrefService* prefs); - - HttpsUpgradesNavigationThrottle( - content::NavigationHandle* handle, - std::unique_ptr blocking_page_factory); - ~HttpsUpgradesNavigationThrottle() override; - - HttpsUpgradesNavigationThrottle(const HttpsUpgradesNavigationThrottle&) = - delete; - HttpsUpgradesNavigationThrottle& operator=( - const HttpsUpgradesNavigationThrottle&) = delete; - - // content::NavigationThrottle: - content::NavigationThrottle::ThrottleCheckResult WillStartRequest() override; - content::NavigationThrottle::ThrottleCheckResult WillRedirectRequest() - override; - content::NavigationThrottle::ThrottleCheckResult WillFailRequest() override; - content::NavigationThrottle::ThrottleCheckResult WillProcessResponse() - override; - const char* GetNameForLogging() override; - - static void set_timeout_for_testing(int timeout_in_seconds); - - private: - std::unique_ptr blocking_page_factory_; -}; - -#endif // CHROME_BROWSER_SSL_HTTPS_UPGRADES_NAVIGATION_THROTTLE_H_ diff --git a/chrome/common/chrome_features.cc b/chrome/common/chrome_features.cc index a8723f6faf731..afc98d10abcef 100644 --- a/chrome/common/chrome_features.cc +++ b/chrome/common/chrome_features.cc @@ -632,11 +632,6 @@ BASE_FEATURE(kHideWebAppOriginText, // Sets whether the HTTPS-Only Mode setting is displayed in the settings UI. BASE_FEATURE(kHttpsOnlyMode, "HttpsOnlyMode", base::FEATURE_ENABLED_BY_DEFAULT); -// Enables the new implementation of HTTPS-First Mode. -BASE_FEATURE(kHttpsFirstModeV2, - "HttpsFirstModeV2", - base::FEATURE_DISABLED_BY_DEFAULT); - // Enables automatically upgrading main frame navigations to HTTPS. BASE_FEATURE(kHttpsUpgrades, "HttpsUpgrades", diff --git a/chrome/common/chrome_features.h b/chrome/common/chrome_features.h index 09dd7cdd8c929..045fdc78cb4c0 100644 --- a/chrome/common/chrome_features.h +++ b/chrome/common/chrome_features.h @@ -370,7 +370,6 @@ BASE_DECLARE_FEATURE(kHappinessTrackingGeneralCamera); COMPONENT_EXPORT(CHROME_FEATURES) BASE_DECLARE_FEATURE(kHideWebAppOriginText); COMPONENT_EXPORT(CHROME_FEATURES) BASE_DECLARE_FEATURE(kHttpsOnlyMode); -COMPONENT_EXPORT(CHROME_FEATURES) BASE_DECLARE_FEATURE(kHttpsFirstModeV2); COMPONENT_EXPORT(CHROME_FEATURES) BASE_DECLARE_FEATURE(kHttpsUpgrades); #if BUILDFLAG(IS_MAC) diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn index 3590b80995558..7757fef3ba4a7 100644 --- a/chrome/test/BUILD.gn +++ b/chrome/test/BUILD.gn @@ -2118,7 +2118,6 @@ if (!is_android) { "../browser/ssl/connection_help_tab_helper_browsertest.cc", "../browser/ssl/crlset_browsertest.cc", "../browser/ssl/https_only_mode_browsertest.cc", - "../browser/ssl/https_upgrades_browsertest.cc", "../browser/ssl/known_interception_disclosure_infobar_browsertest.cc", "../browser/ssl/known_interception_disclosure_ui_browsertest.cc", "../browser/ssl/ocsp_browsertest.cc", diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml index efd947aadb7e5..3b29deaa9c4d8 100644 --- a/tools/metrics/histograms/enums.xml +++ b/tools/metrics/histograms/enums.xml @@ -59356,7 +59356,6 @@ from previous Chrome versions. - @@ -59894,7 +59893,6 @@ from previous Chrome versions. -