ssl_browsertest.cc 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432
  1. // Copyright 2019 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "weblayer/test/weblayer_browser_test.h"
  5. #include "base/files/file_path.h"
  6. #include "base/scoped_observation.h"
  7. #include "base/test/scoped_feature_list.h"
  8. #include "build/build_config.h"
  9. #include "components/network_time/network_time_tracker.h"
  10. #include "components/security_interstitials/content/insecure_form_blocking_page.h"
  11. #include "components/security_interstitials/content/ssl_error_assistant.h"
  12. #include "components/security_interstitials/content/ssl_error_handler.h"
  13. #include "net/ssl/ssl_info.h"
  14. #include "net/test/embedded_test_server/embedded_test_server.h"
  15. #include "third_party/abseil-cpp/absl/types/optional.h"
  16. #include "weblayer/browser/browser_process.h"
  17. #include "weblayer/browser/weblayer_security_blocking_page_factory.h"
  18. #include "weblayer/public/browser.h"
  19. #include "weblayer/public/browser_observer.h"
  20. #include "weblayer/public/error_page.h"
  21. #include "weblayer/public/error_page_delegate.h"
  22. #include "weblayer/public/tab.h"
  23. #include "weblayer/shell/browser/shell.h"
  24. #include "weblayer/test/interstitial_utils.h"
  25. #include "weblayer/test/load_completion_observer.h"
  26. #include "weblayer/test/test_navigation_observer.h"
  27. #include "weblayer/test/weblayer_browser_test_utils.h"
  28. namespace weblayer {
  29. namespace {
  30. #if BUILDFLAG(IS_ANDROID)
  31. // Waits for a new tab to be created, and then load |url|.
  32. class NewTabWaiter : public BrowserObserver {
  33. public:
  34. NewTabWaiter(Browser* browser, const GURL& url) : url_(url) {
  35. observation_.Observe(browser);
  36. }
  37. void OnTabAdded(Tab* tab) override {
  38. navigation_observer_ = std::make_unique<TestNavigationObserver>(
  39. url_, TestNavigationObserver::NavigationEvent::kStart, tab);
  40. run_loop_.Quit();
  41. }
  42. void Wait() {
  43. if (!navigation_observer_)
  44. run_loop_.Run();
  45. navigation_observer_->Wait();
  46. }
  47. private:
  48. GURL url_;
  49. std::unique_ptr<TestNavigationObserver> navigation_observer_;
  50. base::RunLoop run_loop_;
  51. base::ScopedObservation<Browser, BrowserObserver> observation_{this};
  52. };
  53. #endif
  54. class TestErrorPageDelegate : public ErrorPageDelegate {
  55. public:
  56. bool was_get_error_page_content_called() const {
  57. return was_get_error_page_content_called_;
  58. }
  59. // ErrorPageDelegate:
  60. bool OnBackToSafety() override { return false; }
  61. std::unique_ptr<ErrorPage> GetErrorPageContent(
  62. Navigation* navigation) override {
  63. was_get_error_page_content_called_ = true;
  64. return std::make_unique<ErrorPage>();
  65. }
  66. private:
  67. bool was_get_error_page_content_called_ = false;
  68. };
  69. } // namespace
  70. class SSLBrowserTest : public WebLayerBrowserTest {
  71. public:
  72. SSLBrowserTest() = default;
  73. SSLBrowserTest(const SSLBrowserTest&) = delete;
  74. SSLBrowserTest& operator=(const SSLBrowserTest&) = delete;
  75. ~SSLBrowserTest() override = default;
  76. // WebLayerBrowserTest:
  77. void SetUpOnMainThread() override {
  78. https_server_ = std::make_unique<net::EmbeddedTestServer>(
  79. net::EmbeddedTestServer::TYPE_HTTPS);
  80. https_server_->AddDefaultHandlers(
  81. base::FilePath(FILE_PATH_LITERAL("weblayer/test/data")));
  82. https_server_mismatched_ = std::make_unique<net::EmbeddedTestServer>(
  83. net::EmbeddedTestServer::TYPE_HTTPS);
  84. https_server_mismatched_->SetSSLConfig(
  85. net::EmbeddedTestServer::CERT_MISMATCHED_NAME);
  86. https_server_mismatched_->AddDefaultHandlers(
  87. base::FilePath(FILE_PATH_LITERAL("weblayer/test/data")));
  88. https_server_expired_ = std::make_unique<net::EmbeddedTestServer>(
  89. net::EmbeddedTestServer::TYPE_HTTPS);
  90. https_server_expired_->SetSSLConfig(net::EmbeddedTestServer::CERT_EXPIRED);
  91. https_server_expired_->AddDefaultHandlers(
  92. base::FilePath(FILE_PATH_LITERAL("weblayer/test/data")));
  93. ASSERT_TRUE(https_server_->Start());
  94. ASSERT_TRUE(https_server_mismatched_->Start());
  95. ASSERT_TRUE(https_server_expired_->Start());
  96. }
  97. void PostRunTestOnMainThread() override {
  98. https_server_.reset();
  99. https_server_mismatched_.reset();
  100. WebLayerBrowserTest::PostRunTestOnMainThread();
  101. }
  102. void NavigateToOkPage() {
  103. ASSERT_EQ("127.0.0.1", ok_url().host());
  104. NavigateAndWaitForCompletion(ok_url(), shell());
  105. EXPECT_FALSE(IsShowingSecurityInterstitial(shell()->tab()));
  106. }
  107. void NavigateToPageWithMismatchedCertExpectSSLInterstitial() {
  108. // Do a navigation that should result in an SSL error.
  109. NavigateAndWaitForFailure(mismatched_cert_url(), shell());
  110. // First check that there *is* an interstitial.
  111. ASSERT_TRUE(IsShowingSecurityInterstitial(shell()->tab()));
  112. // Now verify that the interstitial is in fact an SSL interstitial.
  113. EXPECT_TRUE(IsShowingSSLInterstitial(shell()->tab()));
  114. // TODO(blundell): Check the security state once security state is available
  115. // via the public WebLayer API, following the example of //chrome's
  116. // ssl_browsertest.cc's CheckAuthenticationBrokenState() function.
  117. }
  118. void NavigateToPageWithMismatchedCertExpectCaptivePortalInterstitial() {
  119. // Do a navigation that should result in an SSL error.
  120. NavigateAndWaitForFailure(mismatched_cert_url(), shell());
  121. // First check that there *is* an interstitial.
  122. ASSERT_TRUE(IsShowingSecurityInterstitial(shell()->tab()));
  123. // Now verify that the interstitial is in fact a captive portal
  124. // interstitial.
  125. EXPECT_TRUE(IsShowingCaptivePortalInterstitial(shell()->tab()));
  126. // TODO(blundell): Check the security state once security state is available
  127. // via the public WebLayer API, following the example of //chrome's
  128. // ssl_browsertest.cc's CheckAuthenticationBrokenState() function.
  129. }
  130. void NavigateToPageWithExpiredCertExpectSSLInterstitial() {
  131. // Do a navigation that should result in an SSL error.
  132. NavigateAndWaitForFailure(expired_cert_url(), shell());
  133. // First check that there *is* an interstitial.
  134. ASSERT_TRUE(IsShowingSecurityInterstitial(shell()->tab()));
  135. // Now verify that the interstitial is in fact an SSL interstitial.
  136. EXPECT_TRUE(IsShowingSSLInterstitial(shell()->tab()));
  137. // TODO(blundell): Check the security state once security state is available
  138. // via the public WebLayer API, following the example of //chrome's
  139. // ssl_browsertest.cc's CheckAuthenticationBrokenState() function.
  140. }
  141. void NavigateToPageWithExpiredCertExpectBadClockInterstitial() {
  142. // Do a navigation that should result in an SSL error.
  143. NavigateAndWaitForFailure(expired_cert_url(), shell());
  144. // First check that there *is* an interstitial.
  145. ASSERT_TRUE(IsShowingSecurityInterstitial(shell()->tab()));
  146. // Now verify that the interstitial is in fact a bad clock interstitial.
  147. EXPECT_TRUE(IsShowingBadClockInterstitial(shell()->tab()));
  148. // TODO(blundell): Check the security state once security state is available
  149. // via the public WebLayer API, following the example of //chrome's
  150. // ssl_browsertest.cc's CheckAuthenticationBrokenState() function.
  151. }
  152. void NavigateToPageWithMismatchedCertExpectNotBlocked() {
  153. NavigateAndWaitForCompletion(mismatched_cert_url(), shell());
  154. EXPECT_FALSE(IsShowingSecurityInterstitial(shell()->tab()));
  155. // TODO(blundell): Check the security state once security state is available
  156. // via the public WebLayer API, following the example of //chrome's
  157. // ssl_browsertest.cc's CheckAuthenticationBrokenState() function.
  158. }
  159. void SendInterstitialNavigationCommandAndWait(
  160. bool proceed,
  161. absl::optional<GURL> previous_url = absl::nullopt) {
  162. GURL expected_url =
  163. proceed ? mismatched_cert_url() : previous_url.value_or(ok_url());
  164. ASSERT_TRUE(IsShowingSSLInterstitial(shell()->tab()));
  165. TestNavigationObserver navigation_observer(
  166. expected_url, TestNavigationObserver::NavigationEvent::kCompletion,
  167. shell());
  168. ExecuteScript(shell(),
  169. "window.certificateErrorPageController." +
  170. std::string(proceed ? "proceed" : "dontProceed") + "();",
  171. false /*use_separate_isolate*/);
  172. navigation_observer.Wait();
  173. EXPECT_FALSE(IsShowingSSLInterstitial(shell()->tab()));
  174. }
  175. void SendInterstitialReloadCommandAndWait() {
  176. ASSERT_TRUE(IsShowingSSLInterstitial(shell()->tab()));
  177. LoadCompletionObserver load_observer(shell());
  178. ExecuteScript(shell(), "window.certificateErrorPageController.reload();",
  179. false /*use_separate_isolate*/);
  180. load_observer.Wait();
  181. // Should still be showing the SSL interstitial after the reload command is
  182. // processed.
  183. EXPECT_TRUE(IsShowingSSLInterstitial(shell()->tab()));
  184. }
  185. #if BUILDFLAG(IS_ANDROID)
  186. void SendInterstitialOpenLoginCommandAndWait() {
  187. ASSERT_TRUE(IsShowingCaptivePortalInterstitial(shell()->tab()));
  188. // Note: The embedded test server cannot actually load the captive portal
  189. // login URL, so simply detect the start of the navigation to the page.
  190. NewTabWaiter waiter(shell()->browser(),
  191. WebLayerSecurityBlockingPageFactory::
  192. GetCaptivePortalLoginPageUrlForTesting());
  193. ExecuteScript(shell(), "window.certificateErrorPageController.openLogin();",
  194. false /*use_separate_isolate*/);
  195. waiter.Wait();
  196. }
  197. #endif
  198. void NavigateToOtherOkPage() {
  199. NavigateAndWaitForCompletion(https_server_->GetURL("/simple_page2.html"),
  200. shell());
  201. EXPECT_FALSE(IsShowingSecurityInterstitial(shell()->tab()));
  202. }
  203. GURL ok_url() { return https_server_->GetURL("/simple_page.html"); }
  204. GURL mismatched_cert_url() {
  205. return https_server_mismatched_->GetURL("/simple_page.html");
  206. }
  207. GURL expired_cert_url() {
  208. return https_server_expired_->GetURL("/simple_page.html");
  209. }
  210. protected:
  211. std::unique_ptr<net::EmbeddedTestServer> https_server_;
  212. std::unique_ptr<net::EmbeddedTestServer> https_server_mismatched_;
  213. std::unique_ptr<net::EmbeddedTestServer> https_server_expired_;
  214. };
  215. // Tests clicking "take me back" on the interstitial page.
  216. IN_PROC_BROWSER_TEST_F(SSLBrowserTest, TakeMeBack) {
  217. NavigateToOkPage();
  218. NavigateToPageWithMismatchedCertExpectSSLInterstitial();
  219. // Click "Take me back".
  220. SendInterstitialNavigationCommandAndWait(false /*proceed*/);
  221. // Check that it's possible to navigate to a new page.
  222. NavigateToOtherOkPage();
  223. // Navigate to the bad SSL page again, an interstitial shows again (in
  224. // contrast to what would happen had the user chosen to proceed).
  225. NavigateToPageWithMismatchedCertExpectSSLInterstitial();
  226. }
  227. // Tests clicking "take me back" on the interstitial page when there's no
  228. // navigation history. The user should be taken to a safe page (about:blank).
  229. IN_PROC_BROWSER_TEST_F(SSLBrowserTest, TakeMeBackEmptyNavigationHistory) {
  230. NavigateToPageWithMismatchedCertExpectSSLInterstitial();
  231. // Click "Take me back".
  232. SendInterstitialNavigationCommandAndWait(false /*proceed*/,
  233. GURL("about:blank"));
  234. }
  235. IN_PROC_BROWSER_TEST_F(SSLBrowserTest, Reload) {
  236. NavigateToOkPage();
  237. NavigateToPageWithMismatchedCertExpectSSLInterstitial();
  238. SendInterstitialReloadCommandAndWait();
  239. // TODO(blundell): Ideally we would fix the SSL error, reload, and verify
  240. // that the SSL interstitial isn't showing. However, currently this doesn't
  241. // work: Calling ResetSSLConfig() on |http_server_mismatched_| passing
  242. // CERT_OK does not cause future reloads or navigations to
  243. // mismatched_cert_url() to succeed; they still fail and pop an interstitial.
  244. // I verified that the LoadCompletionObserver is in fact waiting for a new
  245. // load, i.e., there is actually a *new* SSL interstitial popped up. From
  246. // looking at the ResetSSLConfig() impl there shouldn't be any waiting or
  247. // anything needed within the client.
  248. }
  249. // Tests clicking proceed link on the interstitial page. This is a PRE_ test
  250. // because it also acts as setup for the test below which verifies the behavior
  251. // across restarts.
  252. // TODO(crbug.com/654704): Android does not support PRE_ tests. For Android just
  253. // run only the PRE_ version of this test.
  254. #if BUILDFLAG(IS_ANDROID)
  255. #define PRE_Proceed Proceed
  256. #endif
  257. IN_PROC_BROWSER_TEST_F(SSLBrowserTest, PRE_Proceed) {
  258. NavigateToOkPage();
  259. NavigateToPageWithMismatchedCertExpectSSLInterstitial();
  260. SendInterstitialNavigationCommandAndWait(true /*proceed*/);
  261. // Go back to an OK page, then try to navigate again. The "Proceed" decision
  262. // should be saved, so no interstitial is shown this time.
  263. NavigateToOkPage();
  264. NavigateToPageWithMismatchedCertExpectNotBlocked();
  265. }
  266. #if !BUILDFLAG(IS_ANDROID)
  267. // The proceed decision is perpetuated across WebLayer sessions, i.e. WebLayer
  268. // will not block again when navigating to the same bad page that was previously
  269. // proceeded through.
  270. IN_PROC_BROWSER_TEST_F(SSLBrowserTest, Proceed) {
  271. NavigateToPageWithMismatchedCertExpectNotBlocked();
  272. }
  273. #endif
  274. // Tests navigating away from the interstitial page.
  275. IN_PROC_BROWSER_TEST_F(SSLBrowserTest, NavigateAway) {
  276. NavigateToOkPage();
  277. NavigateToPageWithMismatchedCertExpectSSLInterstitial();
  278. NavigateToOtherOkPage();
  279. }
  280. // Tests the scenario where the OS reports that an SSL error is due to a
  281. // captive portal. A captive portal interstitial should be displayed. The test
  282. // then switches OS captive portal status to false and reloads the page. This
  283. // time, a normal SSL interstitial should be displayed.
  284. IN_PROC_BROWSER_TEST_F(SSLBrowserTest, OSReportsCaptivePortal) {
  285. SSLErrorHandler::SetOSReportsCaptivePortalForTesting(true);
  286. NavigateToPageWithMismatchedCertExpectCaptivePortalInterstitial();
  287. // Check that clearing the test setting causes behavior to revert to normal.
  288. SSLErrorHandler::SetOSReportsCaptivePortalForTesting(false);
  289. NavigateToPageWithMismatchedCertExpectSSLInterstitial();
  290. }
  291. #if BUILDFLAG(IS_ANDROID)
  292. // Tests that after reaching a captive portal interstitial, clicking on the
  293. // connect link will cause a navigation to the login page.
  294. IN_PROC_BROWSER_TEST_F(SSLBrowserTest, CaptivePortalConnectToLoginPage) {
  295. SSLErrorHandler::SetOSReportsCaptivePortalForTesting(true);
  296. NavigateToPageWithMismatchedCertExpectCaptivePortalInterstitial();
  297. SendInterstitialOpenLoginCommandAndWait();
  298. }
  299. #endif
  300. IN_PROC_BROWSER_TEST_F(SSLBrowserTest, BadClockInterstitial) {
  301. // Without the NetworkTimeTracker reporting that the clock is ahead or
  302. // behind, navigating to a page with an expired cert should result in the
  303. // default SSL interstitial appearing.
  304. NavigateToPageWithExpiredCertExpectSSLInterstitial();
  305. // Set network time back ten minutes.
  306. BrowserProcess::GetInstance()->GetNetworkTimeTracker()->UpdateNetworkTime(
  307. base::Time::Now() - base::Minutes(10),
  308. base::Milliseconds(1), /* resolution */
  309. base::Milliseconds(500), /* latency */
  310. base::TimeTicks::Now() /* posting time of this update */);
  311. // Now navigating to a page with an expired cert should cause the bad clock
  312. // interstitial to appear.
  313. NavigateToPageWithExpiredCertExpectBadClockInterstitial();
  314. }
  315. // This test verifies that a certificate in the list of known captive portal
  316. // certificates in ssl_error_assistant.asciipb is detected as such. This serves
  317. // to verify that the ssl_error_assistant proto was correctly loaded.
  318. IN_PROC_BROWSER_TEST_F(SSLBrowserTest,
  319. CertificateInKnownCaptivePortalsListDetected) {
  320. net::SSLInfo ssl_info_with_known_captive_portal_cert;
  321. net::HashValue captive_portal_public_key;
  322. // Set up the SSSLInfo with the certificate of captive-portal.badssl.com
  323. // (taken from ssl_error_assistant.asciipb).
  324. ASSERT_TRUE(captive_portal_public_key.FromString(
  325. "sha256/fjZPHewEHTrMDX3I1ecEIeoy3WFxHyGplOLv28kIbtI="));
  326. net::HashValueVector public_keys;
  327. public_keys.push_back(captive_portal_public_key);
  328. ssl_info_with_known_captive_portal_cert.public_key_hashes = public_keys;
  329. EXPECT_TRUE(SSLErrorAssistant().IsKnownCaptivePortalCertificate(
  330. ssl_info_with_known_captive_portal_cert));
  331. }
  332. // Verifies an error page is not requested for an ssl error.
  333. IN_PROC_BROWSER_TEST_F(SSLBrowserTest, ErrorPageNotCalledForMismatch) {
  334. TestErrorPageDelegate error_page_delegate;
  335. shell()->tab()->SetErrorPageDelegate(&error_page_delegate);
  336. NavigateToOkPage();
  337. EXPECT_FALSE(error_page_delegate.was_get_error_page_content_called());
  338. NavigateToPageWithMismatchedCertExpectSSLInterstitial();
  339. EXPECT_FALSE(error_page_delegate.was_get_error_page_content_called());
  340. }
  341. // Visits a page that displays an insecure form, submits the form, and checks an
  342. // interstitial is shown.
  343. IN_PROC_BROWSER_TEST_F(SSLBrowserTest,
  344. TestDisplaysInsecureFormSubmissionWarning) {
  345. GURL insecure_form_url = https_server_->GetURL("/insecure_form.html");
  346. GURL form_target_url = GURL("http://does-not-exist.test/form_target.html?");
  347. NavigateAndWaitForCompletion(insecure_form_url, shell());
  348. // Submit the form and wait for the interstitial to load.
  349. TestNavigationObserver navigation_observer(
  350. form_target_url, TestNavigationObserver::NavigationEvent::kFailure,
  351. shell());
  352. ExecuteScript(shell(), "submitForm();", false /*use_separate_isolate*/);
  353. navigation_observer.Wait();
  354. // Check the correct interstitial loaded.
  355. EXPECT_TRUE(IsShowingInsecureFormInterstitial(shell()->tab()));
  356. }
  357. } // namespace weblayer