1 // Copyright 2015 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.
5 #include "chrome/browser/ssl/security_state_model.h"
7 #include "base/command_line.h"
8 #include "base/files/file_path.h"
9 #include "base/macros.h"
10 #include "base/prefs/pref_service.h"
11 #include "chrome/browser/ssl/cert_verifier_browser_test.h"
12 #include "chrome/browser/ssl/ssl_blocking_page.h"
13 #include "chrome/browser/ui/browser.h"
14 #include "chrome/browser/ui/tabs/tab_strip_model.h"
15 #include "chrome/common/chrome_paths.h"
16 #include "chrome/common/chrome_switches.h"
17 #include "chrome/common/pref_names.h"
18 #include "chrome/test/base/in_process_browser_test.h"
19 #include "chrome/test/base/ui_test_utils.h"
20 #include "content/public/browser/cert_store.h"
21 #include "content/public/browser/interstitial_page.h"
22 #include "content/public/browser/navigation_controller.h"
23 #include "content/public/browser/notification_service.h"
24 #include "content/public/browser/notification_types.h"
25 #include "content/public/browser/web_contents.h"
26 #include "content/public/common/referrer.h"
27 #include "content/public/test/browser_test_utils.h"
28 #include "net/base/net_errors.h"
29 #include "net/cert/cert_status_flags.h"
30 #include "net/cert/cert_verify_result.h"
31 #include "net/cert/mock_cert_verifier.h"
32 #include "net/cert/x509_certificate.h"
33 #include "net/test/url_request/url_request_failed_job.h"
34 #include "net/url_request/url_request_filter.h"
38 const base::FilePath::CharType kDocRoot
[] =
39 FILE_PATH_LITERAL("chrome/test/data");
41 void CheckSecurityInfoForSecure(
42 content::WebContents
* contents
,
43 SecurityStateModel::SecurityLevel expect_security_level
,
44 SecurityStateModel::SHA1DeprecationStatus expect_sha1_status
,
45 SecurityStateModel::MixedContentStatus expect_mixed_content_status
,
46 bool expect_cert_error
) {
47 ASSERT_TRUE(contents
);
49 SecurityStateModel
* model
= SecurityStateModel::FromWebContents(contents
);
51 const SecurityStateModel::SecurityInfo
& security_info
=
52 model
->GetSecurityInfo();
53 EXPECT_EQ(expect_security_level
, security_info
.security_level
);
54 EXPECT_EQ(expect_sha1_status
, security_info
.sha1_deprecation_status
);
55 EXPECT_EQ(expect_mixed_content_status
, security_info
.mixed_content_status
);
56 EXPECT_TRUE(security_info
.sct_verify_statuses
.empty());
57 EXPECT_TRUE(security_info
.scheme_is_cryptographic
);
58 EXPECT_EQ(expect_cert_error
,
59 net::IsCertStatusError(security_info
.cert_status
));
60 EXPECT_GT(security_info
.security_bits
, 0);
62 content::CertStore
* cert_store
= content::CertStore::GetInstance();
63 scoped_refptr
<net::X509Certificate
> cert
;
64 EXPECT_TRUE(cert_store
->RetrieveCert(security_info
.cert_id
, &cert
));
67 void CheckSecurityInfoForNonSecure(content::WebContents
* contents
) {
68 ASSERT_TRUE(contents
);
70 SecurityStateModel
* model
= SecurityStateModel::FromWebContents(contents
);
72 const SecurityStateModel::SecurityInfo
& security_info
=
73 model
->GetSecurityInfo();
74 EXPECT_EQ(SecurityStateModel::NONE
, security_info
.security_level
);
75 EXPECT_EQ(SecurityStateModel::NO_DEPRECATED_SHA1
,
76 security_info
.sha1_deprecation_status
);
77 EXPECT_EQ(SecurityStateModel::NO_MIXED_CONTENT
,
78 security_info
.mixed_content_status
);
79 EXPECT_TRUE(security_info
.sct_verify_statuses
.empty());
80 EXPECT_FALSE(security_info
.scheme_is_cryptographic
);
81 EXPECT_FALSE(net::IsCertStatusError(security_info
.cert_status
));
82 EXPECT_EQ(-1, security_info
.security_bits
);
83 EXPECT_EQ(0, security_info
.cert_id
);
86 class SecurityStateModelTest
: public CertVerifierBrowserTest
{
88 SecurityStateModelTest()
89 : https_server_(net::SpawnedTestServer::TYPE_HTTPS
,
90 SSLOptions(SSLOptions::CERT_OK
),
91 base::FilePath(kDocRoot
)) {}
93 void SetUpCommandLine(base::CommandLine
* command_line
) override
{
94 // Browser will both run and display insecure content.
95 command_line
->AppendSwitch(switches::kAllowRunningInsecureContent
);
98 void ProceedThroughInterstitial(content::WebContents
* tab
) {
99 content::InterstitialPage
* interstitial_page
= tab
->GetInterstitialPage();
100 ASSERT_TRUE(interstitial_page
);
101 ASSERT_EQ(SSLBlockingPage::kTypeForTesting
,
102 interstitial_page
->GetDelegateForTesting()->GetTypeForTesting());
103 content::WindowedNotificationObserver
observer(
104 content::NOTIFICATION_LOAD_STOP
,
105 content::Source
<content::NavigationController
>(&tab
->GetController()));
106 interstitial_page
->Proceed();
110 static bool GetFilePathWithHostAndPortReplacement(
111 const std::string
& original_file_path
,
112 const net::HostPortPair
& host_port_pair
,
113 std::string
* replacement_path
) {
114 std::vector
<net::SpawnedTestServer::StringPair
> replacement_text
;
115 replacement_text
.push_back(
116 make_pair("REPLACE_WITH_HOST_AND_PORT", host_port_pair
.ToString()));
117 return net::SpawnedTestServer::GetFilePathWithReplacements(
118 original_file_path
, replacement_text
, replacement_path
);
122 void SetUpMockCertVerifierForHttpsServer(net::CertStatus cert_status
,
124 scoped_refptr
<net::X509Certificate
> cert(https_server_
.GetCertificate());
125 net::CertVerifyResult verify_result
;
126 verify_result
.is_issued_by_known_root
= true;
127 verify_result
.verified_cert
= cert
;
128 verify_result
.cert_status
= cert_status
;
130 mock_cert_verifier()->AddResultForCert(cert
.get(), verify_result
,
134 net::SpawnedTestServer https_server_
;
137 typedef net::SpawnedTestServer::SSLOptions SSLOptions
;
139 DISALLOW_COPY_AND_ASSIGN(SecurityStateModelTest
);
142 IN_PROC_BROWSER_TEST_F(SecurityStateModelTest
, HttpPage
) {
143 ASSERT_TRUE(test_server()->Start());
144 ui_test_utils::NavigateToURL(browser(),
145 test_server()->GetURL("files/ssl/google.html"));
146 content::WebContents
* contents
=
147 browser()->tab_strip_model()->GetActiveWebContents();
148 ASSERT_TRUE(contents
);
150 SecurityStateModel
* model
= SecurityStateModel::FromWebContents(contents
);
152 const SecurityStateModel::SecurityInfo
& security_info
=
153 model
->GetSecurityInfo();
154 EXPECT_EQ(SecurityStateModel::NONE
, security_info
.security_level
);
155 EXPECT_EQ(SecurityStateModel::NO_DEPRECATED_SHA1
,
156 security_info
.sha1_deprecation_status
);
157 EXPECT_EQ(SecurityStateModel::NO_MIXED_CONTENT
,
158 security_info
.mixed_content_status
);
159 EXPECT_TRUE(security_info
.sct_verify_statuses
.empty());
160 EXPECT_FALSE(security_info
.scheme_is_cryptographic
);
161 EXPECT_FALSE(net::IsCertStatusError(security_info
.cert_status
));
162 EXPECT_EQ(0, security_info
.cert_id
);
163 EXPECT_EQ(-1, security_info
.security_bits
);
164 EXPECT_EQ(0, security_info
.connection_status
);
167 IN_PROC_BROWSER_TEST_F(SecurityStateModelTest
, HttpsPage
) {
168 ASSERT_TRUE(https_server_
.Start());
169 SetUpMockCertVerifierForHttpsServer(0, net::OK
);
171 ui_test_utils::NavigateToURL(browser(),
172 https_server_
.GetURL("files/ssl/google.html"));
173 CheckSecurityInfoForSecure(
174 browser()->tab_strip_model()->GetActiveWebContents(),
175 SecurityStateModel::SECURE
, SecurityStateModel::NO_DEPRECATED_SHA1
,
176 SecurityStateModel::NO_MIXED_CONTENT
,
177 false /* expect cert status error */);
180 IN_PROC_BROWSER_TEST_F(SecurityStateModelTest
, SHA1Broken
) {
181 ASSERT_TRUE(https_server_
.Start());
182 // The test server uses a long-lived cert by default, so a SHA1
183 // signature in it will register as a "broken" condition rather than
185 SetUpMockCertVerifierForHttpsServer(net::CERT_STATUS_SHA1_SIGNATURE_PRESENT
,
188 ui_test_utils::NavigateToURL(browser(),
189 https_server_
.GetURL("files/ssl/google.html"));
190 CheckSecurityInfoForSecure(
191 browser()->tab_strip_model()->GetActiveWebContents(),
192 SecurityStateModel::SECURITY_ERROR
,
193 SecurityStateModel::DEPRECATED_SHA1_BROKEN
,
194 SecurityStateModel::NO_MIXED_CONTENT
,
195 false /* expect cert status error */);
198 IN_PROC_BROWSER_TEST_F(SecurityStateModelTest
, MixedContent
) {
199 ASSERT_TRUE(test_server()->Start());
200 ASSERT_TRUE(https_server_
.Start());
201 SetUpMockCertVerifierForHttpsServer(0, net::OK
);
203 // Navigate to an HTTPS page that displays mixed content.
204 std::string replacement_path
;
205 ASSERT_TRUE(GetFilePathWithHostAndPortReplacement(
206 "files/ssl/page_displays_insecure_content.html",
207 test_server()->host_port_pair(), &replacement_path
));
208 ui_test_utils::NavigateToURL(browser(),
209 https_server_
.GetURL(replacement_path
));
210 CheckSecurityInfoForSecure(
211 browser()->tab_strip_model()->GetActiveWebContents(),
212 SecurityStateModel::NONE
, SecurityStateModel::NO_DEPRECATED_SHA1
,
213 SecurityStateModel::DISPLAYED_MIXED_CONTENT
,
214 false /* expect cert status error */);
216 // Navigate to an HTTPS page that displays mixed content dynamically.
217 ASSERT_TRUE(GetFilePathWithHostAndPortReplacement(
218 "files/ssl/page_with_dynamic_insecure_content.html",
219 test_server()->host_port_pair(), &replacement_path
));
220 ui_test_utils::NavigateToURL(browser(),
221 https_server_
.GetURL(replacement_path
));
222 CheckSecurityInfoForSecure(
223 browser()->tab_strip_model()->GetActiveWebContents(),
224 SecurityStateModel::SECURE
, SecurityStateModel::NO_DEPRECATED_SHA1
,
225 SecurityStateModel::NO_MIXED_CONTENT
,
226 false /* expect cert status error */);
227 // Load the insecure image.
228 bool js_result
= false;
229 EXPECT_TRUE(content::ExecuteScriptAndExtractBool(
230 browser()->tab_strip_model()->GetActiveWebContents(), "loadBadImage();",
232 EXPECT_TRUE(js_result
);
233 CheckSecurityInfoForSecure(
234 browser()->tab_strip_model()->GetActiveWebContents(),
235 SecurityStateModel::NONE
, SecurityStateModel::NO_DEPRECATED_SHA1
,
236 SecurityStateModel::DISPLAYED_MIXED_CONTENT
,
237 false /* expect cert status error */);
239 // Navigate to an HTTPS page that runs mixed content.
240 ASSERT_TRUE(GetFilePathWithHostAndPortReplacement(
241 "files/ssl/page_runs_insecure_content.html",
242 test_server()->host_port_pair(), &replacement_path
));
243 ui_test_utils::NavigateToURL(browser(),
244 https_server_
.GetURL(replacement_path
));
245 CheckSecurityInfoForSecure(
246 browser()->tab_strip_model()->GetActiveWebContents(),
247 SecurityStateModel::SECURITY_ERROR
,
248 SecurityStateModel::NO_DEPRECATED_SHA1
,
249 SecurityStateModel::RAN_AND_DISPLAYED_MIXED_CONTENT
,
250 false /* expect cert status error */);
253 // Same as the test above but with a long-lived SHA1 cert.
254 IN_PROC_BROWSER_TEST_F(SecurityStateModelTest
, MixedContentWithBrokenSHA1
) {
255 ASSERT_TRUE(test_server()->Start());
256 ASSERT_TRUE(https_server_
.Start());
257 // The test server uses a long-lived cert by default, so a SHA1
258 // signature in it will register as a "broken" condition rather than
260 SetUpMockCertVerifierForHttpsServer(net::CERT_STATUS_SHA1_SIGNATURE_PRESENT
,
263 // Navigate to an HTTPS page that displays mixed content.
264 std::string replacement_path
;
265 ASSERT_TRUE(GetFilePathWithHostAndPortReplacement(
266 "files/ssl/page_displays_insecure_content.html",
267 test_server()->host_port_pair(), &replacement_path
));
268 ui_test_utils::NavigateToURL(browser(),
269 https_server_
.GetURL(replacement_path
));
270 CheckSecurityInfoForSecure(
271 browser()->tab_strip_model()->GetActiveWebContents(),
272 SecurityStateModel::SECURITY_ERROR
,
273 SecurityStateModel::DEPRECATED_SHA1_BROKEN
,
274 SecurityStateModel::DISPLAYED_MIXED_CONTENT
,
275 false /* expect cert status error */);
277 // Navigate to an HTTPS page that displays mixed content dynamically.
278 ASSERT_TRUE(GetFilePathWithHostAndPortReplacement(
279 "files/ssl/page_with_dynamic_insecure_content.html",
280 test_server()->host_port_pair(), &replacement_path
));
281 ui_test_utils::NavigateToURL(browser(),
282 https_server_
.GetURL(replacement_path
));
283 CheckSecurityInfoForSecure(
284 browser()->tab_strip_model()->GetActiveWebContents(),
285 SecurityStateModel::SECURITY_ERROR
,
286 SecurityStateModel::DEPRECATED_SHA1_BROKEN
,
287 SecurityStateModel::NO_MIXED_CONTENT
,
288 false /* expect cert status error */);
289 // Load the insecure image.
290 bool js_result
= false;
291 EXPECT_TRUE(content::ExecuteScriptAndExtractBool(
292 browser()->tab_strip_model()->GetActiveWebContents(), "loadBadImage();",
294 EXPECT_TRUE(js_result
);
295 CheckSecurityInfoForSecure(
296 browser()->tab_strip_model()->GetActiveWebContents(),
297 SecurityStateModel::SECURITY_ERROR
,
298 SecurityStateModel::DEPRECATED_SHA1_BROKEN
,
299 SecurityStateModel::DISPLAYED_MIXED_CONTENT
,
300 false /* expect cert status error */);
302 // Navigate to an HTTPS page that runs mixed content.
303 ASSERT_TRUE(GetFilePathWithHostAndPortReplacement(
304 "files/ssl/page_runs_insecure_content.html",
305 test_server()->host_port_pair(), &replacement_path
));
306 ui_test_utils::NavigateToURL(browser(),
307 https_server_
.GetURL(replacement_path
));
308 CheckSecurityInfoForSecure(
309 browser()->tab_strip_model()->GetActiveWebContents(),
310 SecurityStateModel::SECURITY_ERROR
,
311 SecurityStateModel::DEPRECATED_SHA1_BROKEN
,
312 SecurityStateModel::RAN_AND_DISPLAYED_MIXED_CONTENT
,
313 false /* expect cert status error */);
316 IN_PROC_BROWSER_TEST_F(SecurityStateModelTest
, BrokenHTTPS
) {
317 ASSERT_TRUE(test_server()->Start());
318 ASSERT_TRUE(https_server_
.Start());
319 SetUpMockCertVerifierForHttpsServer(net::CERT_STATUS_DATE_INVALID
,
320 net::ERR_CERT_DATE_INVALID
);
322 ui_test_utils::NavigateToURL(browser(),
323 https_server_
.GetURL("files/ssl/google.html"));
324 CheckSecurityInfoForSecure(
325 browser()->tab_strip_model()->GetActiveWebContents(),
326 SecurityStateModel::SECURITY_ERROR
,
327 SecurityStateModel::NO_DEPRECATED_SHA1
,
328 SecurityStateModel::NO_MIXED_CONTENT
,
329 true /* expect cert status error */);
331 ProceedThroughInterstitial(
332 browser()->tab_strip_model()->GetActiveWebContents());
334 CheckSecurityInfoForSecure(
335 browser()->tab_strip_model()->GetActiveWebContents(),
336 SecurityStateModel::SECURITY_ERROR
,
337 SecurityStateModel::NO_DEPRECATED_SHA1
,
338 SecurityStateModel::NO_MIXED_CONTENT
,
339 true /* expect cert status error */);
341 // Navigate to a broken HTTPS page that displays mixed content.
342 std::string replacement_path
;
343 ASSERT_TRUE(GetFilePathWithHostAndPortReplacement(
344 "files/ssl/page_displays_insecure_content.html",
345 test_server()->host_port_pair(), &replacement_path
));
346 ui_test_utils::NavigateToURL(browser(),
347 https_server_
.GetURL(replacement_path
));
348 CheckSecurityInfoForSecure(
349 browser()->tab_strip_model()->GetActiveWebContents(),
350 SecurityStateModel::SECURITY_ERROR
,
351 SecurityStateModel::NO_DEPRECATED_SHA1
,
352 SecurityStateModel::DISPLAYED_MIXED_CONTENT
,
353 true /* expect cert status error */);
356 // Fails requests with ERR_IO_PENDING. Can be used to simulate a navigation
357 // that never stops loading.
358 class PendingJobInterceptor
: public net::URLRequestInterceptor
{
360 PendingJobInterceptor() {}
361 ~PendingJobInterceptor() override
{}
363 // URLRequestInterceptor implementation
364 net::URLRequestJob
* MaybeInterceptRequest(
365 net::URLRequest
* request
,
366 net::NetworkDelegate
* network_delegate
) const override
{
367 return new net::URLRequestFailedJob(request
, network_delegate
,
368 net::ERR_IO_PENDING
);
372 DISALLOW_COPY_AND_ASSIGN(PendingJobInterceptor
);
375 void InstallLoadingInterceptor(const std::string
& host
) {
376 net::URLRequestFilter
* filter
= net::URLRequestFilter::GetInstance();
377 filter
->AddHostnameInterceptor(
379 scoped_ptr
<net::URLRequestInterceptor
>(new PendingJobInterceptor()));
382 class SecurityStateModelLoadingTest
: public SecurityStateModelTest
{
384 SecurityStateModelLoadingTest() : SecurityStateModelTest() {}
385 ~SecurityStateModelLoadingTest() override
{};
388 void SetUpOnMainThread() override
{
389 content::BrowserThread::PostTask(
390 content::BrowserThread::IO
, FROM_HERE
,
391 base::Bind(&InstallLoadingInterceptor
,
392 test_server()->GetURL("/").host()));
395 DISALLOW_COPY_AND_ASSIGN(SecurityStateModelLoadingTest
);
398 // Tests that navigation state changes cause the security state to be
400 IN_PROC_BROWSER_TEST_F(SecurityStateModelLoadingTest
, NavigationStateChanges
) {
401 ASSERT_TRUE(https_server_
.Start());
402 SetUpMockCertVerifierForHttpsServer(0, net::OK
);
403 ASSERT_TRUE(test_server()->Start());
405 // Navigate to an HTTPS page.
406 ui_test_utils::NavigateToURL(browser(),
407 https_server_
.GetURL("files/ssl/google.html"));
408 CheckSecurityInfoForSecure(
409 browser()->tab_strip_model()->GetActiveWebContents(),
410 SecurityStateModel::SECURE
, SecurityStateModel::NO_DEPRECATED_SHA1
,
411 SecurityStateModel::NO_MIXED_CONTENT
,
412 false /* expect cert status error */);
414 // Navigate to a page that doesn't finish loading. Test that the
415 // security state is neutral while the page is loading.
416 browser()->OpenURL(content::OpenURLParams(test_server()->GetURL("/"),
417 content::Referrer(), CURRENT_TAB
,
418 ui::PAGE_TRANSITION_TYPED
, false));
419 CheckSecurityInfoForNonSecure(
420 browser()->tab_strip_model()->GetActiveWebContents());
423 // Tests that the SecurityStateModel for a WebContents is up-to-date
424 // when the WebContents is inserted into a Browser's TabStripModel.
425 IN_PROC_BROWSER_TEST_F(SecurityStateModelTest
, AddedTab
) {
426 ASSERT_TRUE(https_server_
.Start());
427 SetUpMockCertVerifierForHttpsServer(0, net::OK
);
429 content::WebContents
* tab
=
430 browser()->tab_strip_model()->GetActiveWebContents();
433 content::WebContents
* new_contents
= content::WebContents::Create(
434 content::WebContents::CreateParams(tab
->GetBrowserContext()));
435 content::NavigationController
& controller
= new_contents
->GetController();
436 SecurityStateModel::CreateForWebContents(new_contents
);
437 CheckSecurityInfoForNonSecure(new_contents
);
438 controller
.LoadURL(https_server_
.GetURL("/"), content::Referrer(),
439 ui::PAGE_TRANSITION_TYPED
, std::string());
440 EXPECT_TRUE(content::WaitForLoadStop(new_contents
));
441 CheckSecurityInfoForSecure(new_contents
, SecurityStateModel::SECURE
,
442 SecurityStateModel::NO_DEPRECATED_SHA1
,
443 SecurityStateModel::NO_MIXED_CONTENT
,
444 false /* expect cert status error */);
446 browser()->tab_strip_model()->InsertWebContentsAt(0, new_contents
,
447 TabStripModel::ADD_NONE
);
448 CheckSecurityInfoForSecure(new_contents
, SecurityStateModel::SECURE
,
449 SecurityStateModel::NO_DEPRECATED_SHA1
,
450 SecurityStateModel::NO_MIXED_CONTENT
,
451 false /* expect cert status error */);
454 // TODO(estark): https://crbug.com/530359
455 // Test the following cases:
456 // - warning SHA1 (2016 expiration)
457 // - active mixed content + warning SHA1
458 // - broken HTTPS + warning SHA1