OLD | NEW |
---|---|
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include <vector> | 5 #include <vector> |
6 | 6 |
7 #include "chrome/browser/ssl/ssl_error_classification.h" | 7 #include "chrome/browser/ssl/ssl_error_classification.h" |
8 | 8 |
9 #include "base/build_time.h" | 9 #include "base/build_time.h" |
10 #include "base/metrics/field_trial.h" | 10 #include "base/metrics/field_trial.h" |
11 #include "base/metrics/histogram.h" | 11 #include "base/metrics/histogram.h" |
12 #include "base/strings/string_split.h" | 12 #include "base/strings/string_split.h" |
13 #include "base/strings/utf_string_conversions.h" | 13 #include "base/strings/utf_string_conversions.h" |
14 #include "base/time/time.h" | 14 #include "base/time/time.h" |
15 #include "chrome/browser/browser_process.h" | |
16 #include "chrome/browser/chrome_notification_types.h" | |
17 #include "chrome/browser/profiles/profile.h" | |
15 #include "chrome/browser/ssl/ssl_error_info.h" | 18 #include "chrome/browser/ssl/ssl_error_info.h" |
19 #include "content/public/browser/notification_service.h" | |
20 #include "content/public/browser/web_contents.h" | |
16 #include "net/base/net_util.h" | 21 #include "net/base/net_util.h" |
17 #include "net/base/registry_controlled_domains/registry_controlled_domain.h" | 22 #include "net/base/registry_controlled_domains/registry_controlled_domain.h" |
18 #include "net/cert/x509_cert_types.h" | 23 #include "net/cert/x509_cert_types.h" |
19 #include "net/cert/x509_certificate.h" | 24 #include "net/cert/x509_certificate.h" |
20 #include "url/gurl.h" | 25 #include "url/gurl.h" |
21 | 26 |
22 using base::Time; | 27 #if defined(ENABLE_CAPTIVE_PORTAL_DETECTION) |
23 using base::TimeTicks; | 28 #include "chrome/browser/captive_portal/captive_portal_service.h" |
24 using base::TimeDelta; | 29 #include "chrome/browser/captive_portal/captive_portal_service_factory.h" |
30 #endif | |
25 | 31 |
26 #if defined(OS_WIN) | 32 #if defined(OS_WIN) |
27 #include "base/win/windows_version.h" | 33 #include "base/win/windows_version.h" |
28 #endif | 34 #endif |
29 | 35 |
36 using base::Time; | |
37 using base::TimeTicks; | |
38 using base::TimeDelta; | |
39 | |
30 namespace { | 40 namespace { |
31 | 41 |
32 // Events for UMA. Do not reorder or change! | 42 // Events for UMA. Do not reorder or change! |
33 enum SSLInterstitialCause { | 43 enum SSLInterstitialCause { |
34 CLOCK_PAST, | 44 CLOCK_PAST, |
35 CLOCK_FUTURE, | 45 CLOCK_FUTURE, |
36 WWW_SUBDOMAIN_MATCH, | 46 WWW_SUBDOMAIN_MATCH, |
37 SUBDOMAIN_MATCH, | 47 SUBDOMAIN_MATCH, |
38 SUBDOMAIN_INVERSE_MATCH, | 48 SUBDOMAIN_INVERSE_MATCH, |
39 SUBDOMAIN_OUTSIDE_WILDCARD, | 49 SUBDOMAIN_OUTSIDE_WILDCARD, |
40 HOST_NAME_NOT_KNOWN_TLD, | 50 HOST_NAME_NOT_KNOWN_TLD, |
41 UNUSED_INTERSTITIAL_CAUSE_ENTRY, | 51 UNUSED_INTERSTITIAL_CAUSE_ENTRY, |
42 }; | 52 }; |
43 | 53 |
54 // Events for UMA. Do not reorder or change! | |
55 enum SSLInterstitialCauseCaptivePortal { | |
56 CAPTIVE_PORTAL_DETECTION_ENABLED, | |
57 CAPTIVE_PORTAL_DETECTION_ENABLED_OVERRIDABLE, | |
58 CAPTIVE_PORTAL_PROBE_COMPLETED, | |
59 CAPTIVE_PORTAL_PROBE_COMPLETED_OVERRIDABLE, | |
60 CAPTIVE_PORTAL_NO_RESPONSE, | |
61 CAPTIVE_PORTAL_NO_RESPONSE_OVERRIDABLE, | |
62 CAPTIVE_PORTAL_DETECTED, | |
63 CAPTIVE_PORTAL_DETECTED_OVERRIDABLE, | |
64 UNUSED_CAPTIVE_PORTAL_EVENT, | |
65 }; | |
66 | |
44 // Scores/weights which will be constant through all the SSL error types. | 67 // Scores/weights which will be constant through all the SSL error types. |
45 static const float kServerWeight = 0.5f; | 68 static const float kServerWeight = 0.5f; |
46 static const float kClientWeight = 0.5f; | 69 static const float kClientWeight = 0.5f; |
47 | 70 |
48 void RecordSSLInterstitialCause(bool overridable, SSLInterstitialCause event) { | 71 void RecordSSLInterstitialCause(bool overridable, SSLInterstitialCause event) { |
49 if (overridable) { | 72 if (overridable) { |
50 UMA_HISTOGRAM_ENUMERATION("interstitial.ssl.cause.overridable", event, | 73 UMA_HISTOGRAM_ENUMERATION("interstitial.ssl.cause.overridable", event, |
51 UNUSED_INTERSTITIAL_CAUSE_ENTRY); | 74 UNUSED_INTERSTITIAL_CAUSE_ENTRY); |
52 } else { | 75 } else { |
53 UMA_HISTOGRAM_ENUMERATION("interstitial.ssl.cause.nonoverridable", event, | 76 UMA_HISTOGRAM_ENUMERATION("interstitial.ssl.cause.nonoverridable", event, |
54 UNUSED_INTERSTITIAL_CAUSE_ENTRY); | 77 UNUSED_INTERSTITIAL_CAUSE_ENTRY); |
55 } | 78 } |
56 } | 79 } |
57 | 80 |
81 void RecordCaptivePortalEventStats(SSLInterstitialCauseCaptivePortal event) { | |
82 UMA_HISTOGRAM_ENUMERATION("interstitial.ssl.captive_portal", | |
83 event, | |
84 UNUSED_CAPTIVE_PORTAL_EVENT); | |
85 } | |
86 | |
58 } // namespace | 87 } // namespace |
59 | 88 |
60 SSLErrorClassification::SSLErrorClassification( | 89 SSLErrorClassification::SSLErrorClassification( |
90 content::WebContents* web_contents, | |
61 const base::Time& current_time, | 91 const base::Time& current_time, |
62 const GURL& url, | 92 const GURL& url, |
93 int cert_error, | |
63 const net::X509Certificate& cert) | 94 const net::X509Certificate& cert) |
64 : current_time_(current_time), | 95 : web_contents_(web_contents), |
96 current_time_(current_time), | |
65 request_url_(url), | 97 request_url_(url), |
66 cert_(cert) { } | 98 cert_error_(cert_error), |
99 cert_(cert), | |
100 captive_portal_detection_enabled_(false), | |
101 captive_portal_probe_completed_(false), | |
102 captive_portal_no_response_(false), | |
103 captive_portal_detected_(false) { | |
104 #if defined(ENABLE_CAPTIVE_PORTAL_DETECTION) | |
105 Profile* profile = Profile::FromBrowserContext( | |
106 web_contents_->GetBrowserContext()); | |
107 CaptivePortalService* captive_portal_service = | |
108 CaptivePortalServiceFactory::GetForProfile(profile); | |
109 captive_portal_detection_enabled_ = captive_portal_service ->enabled(); | |
felt
2014/08/07 23:17:10
lines 109 and 110 both have extra spaces before th
radhikabhar
2014/08/07 23:25:22
Done.
| |
110 captive_portal_service ->DetectCaptivePortal(); | |
111 registrar_.Add(this, | |
112 chrome::NOTIFICATION_CAPTIVE_PORTAL_CHECK_RESULT, | |
113 content::Source<Profile>(profile)); | |
114 #endif | |
115 } | |
67 | 116 |
68 SSLErrorClassification::~SSLErrorClassification() { } | 117 SSLErrorClassification::~SSLErrorClassification() { } |
69 | 118 |
70 float SSLErrorClassification::InvalidDateSeverityScore( | 119 void SSLErrorClassification::RecordCaptivePortalUMAStatistics( |
71 int cert_error) const { | 120 bool overridable) const { |
121 #if defined(ENABLE_CAPTIVE_PORTAL_DETECTION) | |
122 if (captive_portal_detection_enabled_) | |
123 RecordCaptivePortalEventStats( | |
124 overridable ? | |
125 CAPTIVE_PORTAL_DETECTION_ENABLED_OVERRIDABLE : | |
126 CAPTIVE_PORTAL_DETECTION_ENABLED); | |
127 if (captive_portal_probe_completed_) | |
128 RecordCaptivePortalEventStats( | |
129 overridable ? | |
130 CAPTIVE_PORTAL_PROBE_COMPLETED_OVERRIDABLE : | |
131 CAPTIVE_PORTAL_PROBE_COMPLETED); | |
132 // Log only one of portal detected and no response results. | |
133 if (captive_portal_detected_) | |
134 RecordCaptivePortalEventStats( | |
135 overridable ? | |
136 CAPTIVE_PORTAL_DETECTED_OVERRIDABLE : | |
137 CAPTIVE_PORTAL_DETECTED); | |
138 else if (captive_portal_no_response_) | |
139 RecordCaptivePortalEventStats( | |
140 overridable ? | |
141 CAPTIVE_PORTAL_NO_RESPONSE_OVERRIDABLE : | |
142 CAPTIVE_PORTAL_NO_RESPONSE); | |
143 #endif | |
144 } | |
145 | |
146 void SSLErrorClassification::InvalidDateSeverityScore() const { | |
72 SSLErrorInfo::ErrorType type = | 147 SSLErrorInfo::ErrorType type = |
73 SSLErrorInfo::NetErrorToErrorType(cert_error); | 148 SSLErrorInfo::NetErrorToErrorType(cert_error_); |
74 DCHECK(type == SSLErrorInfo::CERT_DATE_INVALID); | 149 DCHECK(type == SSLErrorInfo::CERT_DATE_INVALID); |
150 | |
75 // Client-side characteristics. Check whether or not the system's clock is | 151 // Client-side characteristics. Check whether or not the system's clock is |
76 // wrong and whether or not the user has already encountered this error | 152 // wrong and whether or not the user has encountered this error before. |
77 // before. | |
78 float severity_date_score = 0.0f; | 153 float severity_date_score = 0.0f; |
79 | 154 |
80 static const float kCertificateExpiredWeight = 0.3f; | 155 static const float kCertificateExpiredWeight = 0.3f; |
81 static const float kNotYetValidWeight = 0.2f; | 156 static const float kNotYetValidWeight = 0.2f; |
82 | 157 |
83 static const float kSystemClockWeight = 0.75f; | 158 static const float kSystemClockWeight = 0.75f; |
84 static const float kSystemClockWrongWeight = 0.1f; | 159 static const float kSystemClockWrongWeight = 0.1f; |
85 static const float kSystemClockRightWeight = 1.0f; | 160 static const float kSystemClockRightWeight = 1.0f; |
86 | 161 |
87 if (IsUserClockInThePast(current_time_) || | 162 if (IsUserClockInThePast(current_time_) || |
88 IsUserClockInTheFuture(current_time_)) { | 163 IsUserClockInTheFuture(current_time_)) { |
89 severity_date_score += kClientWeight * kSystemClockWeight * | 164 severity_date_score += kClientWeight * kSystemClockWeight * |
90 kSystemClockWrongWeight; | 165 kSystemClockWrongWeight; |
91 } else { | 166 } else { |
92 severity_date_score += kClientWeight * kSystemClockWeight * | 167 severity_date_score += kClientWeight * kSystemClockWeight * |
93 kSystemClockRightWeight; | 168 kSystemClockRightWeight; |
94 } | 169 } |
95 // TODO(radhikabhar): (crbug.com/393262) Check website settings. | 170 // TODO(radhikabhar): (crbug.com/393262) Check website settings. |
96 | 171 |
97 // Server-side characteristics. Check whether the certificate has expired or | 172 // Server-side characteristics. Check whether the certificate has expired or |
98 // is not yet valid. If the certificate has expired then factor the time which | 173 // is not yet valid. If the certificate has expired then factor the time which |
99 // has passed since expiry. | 174 // has passed since expiry. |
100 if (cert_.HasExpired()) { | 175 if (cert_.HasExpired()) { |
101 severity_date_score += kServerWeight * kCertificateExpiredWeight * | 176 severity_date_score += kServerWeight * kCertificateExpiredWeight * |
102 CalculateScoreTimePassedSinceExpiry(); | 177 CalculateScoreTimePassedSinceExpiry(); |
103 } | 178 } |
104 if (current_time_ < cert_.valid_start()) | 179 if (current_time_ < cert_.valid_start()) |
105 severity_date_score += kServerWeight * kNotYetValidWeight; | 180 severity_date_score += kServerWeight * kNotYetValidWeight; |
106 return severity_date_score; | 181 // TODO(radhikabhar): Record the severity score in a histogram. This will be |
182 // in the next CL - just called the function in ssl_blocking_page.cc. | |
107 } | 183 } |
108 | 184 |
109 float SSLErrorClassification::InvalidCommonNameSeverityScore( | 185 void SSLErrorClassification::InvalidCommonNameSeverityScore() const { |
110 int cert_error) const { | |
111 SSLErrorInfo::ErrorType type = | 186 SSLErrorInfo::ErrorType type = |
112 SSLErrorInfo::NetErrorToErrorType(cert_error); | 187 SSLErrorInfo::NetErrorToErrorType(cert_error_); |
113 DCHECK(type == SSLErrorInfo::CERT_COMMON_NAME_INVALID); | 188 DCHECK(type == SSLErrorInfo::CERT_COMMON_NAME_INVALID); |
114 float severity_name_score = 0.0f; | 189 float severity_name_score = 0.0f; |
115 | 190 |
116 static const float kWWWDifferenceWeight = 0.3f; | 191 static const float kWWWDifferenceWeight = 0.3f; |
117 static const float kSubDomainWeight = 0.2f; | 192 static const float kNameUnderAnyNamesWeight = 0.2f; |
118 static const float kSubDomainInverseWeight = 1.0f; | 193 static const float kAnyNamesUnderNamesWeight = 1.0f; |
119 | 194 |
120 std::string host_name = request_url_.host(); | 195 std::string host_name = request_url_.host(); |
121 if (IsHostNameKnownTLD(host_name)) { | 196 if (IsHostNameKnownTLD(host_name)) { |
122 Tokens host_name_tokens = Tokenize(host_name); | 197 Tokens host_name_tokens = Tokenize(host_name); |
123 if (IsWWWSubDomainMatch()) | 198 if (IsWWWSubDomainMatch()) |
124 severity_name_score += kServerWeight * kWWWDifferenceWeight; | 199 severity_name_score += kServerWeight * kWWWDifferenceWeight; |
125 if (IsSubDomainOutsideWildcard(host_name_tokens)) | 200 if (IsSubDomainOutsideWildcard(host_name_tokens)) |
126 severity_name_score += kServerWeight * kWWWDifferenceWeight; | 201 severity_name_score += kServerWeight * kWWWDifferenceWeight; |
127 | 202 |
128 std::vector<std::string> dns_names; | 203 std::vector<std::string> dns_names; |
129 cert_.GetDNSNames(&dns_names); | 204 cert_.GetDNSNames(&dns_names); |
130 std::vector<Tokens> dns_name_tokens = GetTokenizedDNSNames(dns_names); | 205 std::vector<Tokens> dns_name_tokens = GetTokenizedDNSNames(dns_names); |
131 if (NameUnderAnyNames(host_name_tokens, dns_name_tokens)) | 206 if (NameUnderAnyNames(host_name_tokens, dns_name_tokens)) |
132 severity_name_score += kServerWeight * kSubDomainWeight; | 207 severity_name_score += kServerWeight * kNameUnderAnyNamesWeight; |
133 // Inverse case is more likely to be a MITM attack. | 208 // Inverse case is more likely to be a MITM attack. |
134 if (AnyNamesUnderName(dns_name_tokens, host_name_tokens)) | 209 if (AnyNamesUnderName(dns_name_tokens, host_name_tokens)) |
135 severity_name_score += kServerWeight * kSubDomainInverseWeight; | 210 severity_name_score += kServerWeight * kAnyNamesUnderNamesWeight; |
136 } | 211 } |
137 return severity_name_score; | 212 |
213 static const float kEnvironmentWeight = 0.25f; | |
214 | |
215 severity_name_score += kClientWeight * kEnvironmentWeight * | |
216 CalculateScoreEnvironments(); | |
217 // TODO(radhikabhar): Record the severity score in a histogram. Same as above | |
218 // - this will be in the next CL. So just called the function in the | |
219 // ssl_blocking_page.cc. | |
138 } | 220 } |
139 | 221 |
140 void SSLErrorClassification::RecordUMAStatistics(bool overridable, | 222 void SSLErrorClassification::RecordUMAStatistics( |
141 int cert_error) { | 223 bool overridable) const { |
142 SSLErrorInfo::ErrorType type = | 224 SSLErrorInfo::ErrorType type = |
143 SSLErrorInfo::NetErrorToErrorType(cert_error); | 225 SSLErrorInfo::NetErrorToErrorType(cert_error_); |
144 switch (type) { | 226 switch (type) { |
145 case SSLErrorInfo::CERT_DATE_INVALID: { | 227 case SSLErrorInfo::CERT_DATE_INVALID: { |
146 if (IsUserClockInThePast(base::Time::NowFromSystemTime())) | 228 if (IsUserClockInThePast(base::Time::NowFromSystemTime())) |
147 RecordSSLInterstitialCause(overridable, CLOCK_PAST); | 229 RecordSSLInterstitialCause(overridable, CLOCK_PAST); |
148 if (IsUserClockInTheFuture(base::Time::NowFromSystemTime())) | 230 if (IsUserClockInTheFuture(base::Time::NowFromSystemTime())) |
149 RecordSSLInterstitialCause(overridable, CLOCK_FUTURE); | 231 RecordSSLInterstitialCause(overridable, CLOCK_FUTURE); |
150 break; | 232 break; |
151 } | 233 } |
152 case SSLErrorInfo::CERT_COMMON_NAME_INVALID: { | 234 case SSLErrorInfo::CERT_COMMON_NAME_INVALID: { |
153 std::string host_name = request_url_.host(); | 235 std::string host_name = request_url_.host(); |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
189 static const float kMediumThresholdWeight = 0.3f; | 271 static const float kMediumThresholdWeight = 0.3f; |
190 static const float kLowThresholdWeight = 0.2f; | 272 static const float kLowThresholdWeight = 0.2f; |
191 if (time_passed >= kHighThreshold) | 273 if (time_passed >= kHighThreshold) |
192 return kHighThresholdWeight; | 274 return kHighThresholdWeight; |
193 else if (time_passed >= kLowThreshold) | 275 else if (time_passed >= kLowThreshold) |
194 return kMediumThresholdWeight; | 276 return kMediumThresholdWeight; |
195 else | 277 else |
196 return kLowThresholdWeight; | 278 return kLowThresholdWeight; |
197 } | 279 } |
198 | 280 |
281 float SSLErrorClassification::CalculateScoreEnvironments() const { | |
282 static const float kWifiWeight = 0.7f; | |
283 static const float kCellularWeight = 0.7f; | |
284 static const float kHotspotWeight = 0.2f; | |
285 static const float kEthernetWeight = 0.7f; | |
286 static const float kOtherWeight = 0.7f; | |
287 net::NetworkChangeNotifier::ConnectionType type = | |
288 net::NetworkChangeNotifier::GetConnectionType(); | |
289 if (type == net::NetworkChangeNotifier::CONNECTION_WIFI) | |
290 return kWifiWeight; | |
291 if (type == net::NetworkChangeNotifier::CONNECTION_2G || | |
292 type == net::NetworkChangeNotifier::CONNECTION_3G || | |
293 type == net::NetworkChangeNotifier::CONNECTION_4G ) { | |
294 return kCellularWeight; | |
295 } | |
296 if (type == net::NetworkChangeNotifier::CONNECTION_ETHERNET) | |
297 return kEthernetWeight; | |
298 #if defined(ENABLE_CAPTIVE_PORTAL_DETECTION) | |
299 // Assume if captive portals are detected then the user is connected using a | |
300 // hot spot. | |
301 if (captive_portal_probe_completed_ && captive_portal_detected_) | |
302 return kHotspotWeight; | |
303 #endif | |
304 return kOtherWeight; | |
305 } | |
306 | |
199 bool SSLErrorClassification::IsUserClockInThePast(const base::Time& time_now) { | 307 bool SSLErrorClassification::IsUserClockInThePast(const base::Time& time_now) { |
200 base::Time build_time = base::GetBuildTime(); | 308 base::Time build_time = base::GetBuildTime(); |
201 if (time_now < build_time - base::TimeDelta::FromDays(2)) | 309 if (time_now < build_time - base::TimeDelta::FromDays(2)) |
202 return true; | 310 return true; |
203 return false; | 311 return false; |
204 } | 312 } |
205 | 313 |
206 bool SSLErrorClassification::IsUserClockInTheFuture( | 314 bool SSLErrorClassification::IsUserClockInTheFuture( |
207 const base::Time& time_now) { | 315 const base::Time& time_now) { |
208 base::Time build_time = base::GetBuildTime(); | 316 base::Time build_time = base::GetBuildTime(); |
(...skipping 150 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
359 | 467 |
360 // Move past the "*.". | 468 // Move past the "*.". |
361 std::string extracted_dns_name = name.substr(2); | 469 std::string extracted_dns_name = name.substr(2); |
362 if (FindSubDomainDifference( | 470 if (FindSubDomainDifference( |
363 host_name_tokens, Tokenize(extracted_dns_name)) == 2) { | 471 host_name_tokens, Tokenize(extracted_dns_name)) == 2) { |
364 return true; | 472 return true; |
365 } | 473 } |
366 } | 474 } |
367 return result; | 475 return result; |
368 } | 476 } |
477 | |
478 void SSLErrorClassification::Observe( | |
479 int type, | |
480 const content::NotificationSource& source, | |
481 const content::NotificationDetails& details) { | |
482 #if defined(ENABLE_CAPTIVE_PORTAL_DETECTION) | |
483 // When detection is disabled, captive portal service always sends | |
484 // RESULT_INTERNET_CONNECTED. Ignore any probe results in that case. | |
485 if (!captive_portal_detection_enabled_) | |
486 return; | |
487 if (type == chrome::NOTIFICATION_CAPTIVE_PORTAL_CHECK_RESULT) { | |
488 captive_portal_probe_completed_ = true; | |
489 CaptivePortalService::Results* results = | |
490 content::Details<CaptivePortalService::Results>( | |
491 details).ptr(); | |
492 // If a captive portal was detected at any point when the interstitial was | |
493 // displayed, assume that the interstitial was caused by a captive portal. | |
494 // Example scenario: | |
495 // 1- Interstitial displayed and captive portal detected, setting the flag. | |
496 // 2- Captive portal detection automatically opens portal login page. | |
497 // 3- User logs in on the portal login page. | |
498 // A notification will be received here for RESULT_INTERNET_CONNECTED. Make | |
499 // sure we don't clear the captive protal flag, since the interstitial was | |
500 // potentially caused by the captive portal. | |
501 captive_portal_detected_ = captive_portal_detected_ || | |
502 (results->result == captive_portal::RESULT_BEHIND_CAPTIVE_PORTAL); | |
503 // Also keep track of non-HTTP portals and error cases. | |
504 captive_portal_no_response_ = captive_portal_no_response_ || | |
505 (results->result == captive_portal::RESULT_NO_RESPONSE); | |
506 } | |
507 #endif | |
508 } | |
OLD | NEW |