| OLD | NEW |
| 1 // Copyright 2017 The Chromium Authors. All rights reserved. | 1 // Copyright 2017 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 "components/safe_browsing/password_protection/password_protection_servi
ce.h" | 5 #include "components/safe_browsing/password_protection/password_protection_servi
ce.h" |
| 6 | 6 |
| 7 #include "base/bind.h" | 7 #include "base/bind.h" |
| 8 #include "base/callback.h" | 8 #include "base/callback.h" |
| 9 #include "base/memory/ptr_util.h" | 9 #include "base/memory/ptr_util.h" |
| 10 #include "base/metrics/field_trial.h" | 10 #include "base/metrics/field_trial.h" |
| 11 #include "base/metrics/histogram_macros.h" | 11 #include "base/metrics/histogram_macros.h" |
| 12 #include "base/strings/string_number_conversions.h" | 12 #include "base/strings/string_number_conversions.h" |
| 13 #include "base/strings/string_split.h" | 13 #include "base/strings/string_split.h" |
| 14 #include "base/strings/string_util.h" | 14 #include "base/strings/string_util.h" |
| 15 #include "components/content_settings/core/browser/host_content_settings_map.h" | 15 #include "components/content_settings/core/browser/host_content_settings_map.h" |
| 16 #include "components/history/core/browser/history_service.h" | 16 #include "components/history/core/browser/history_service.h" |
| 17 #include "components/safe_browsing/password_protection/password_protection_reque
st.h" | 17 #include "components/safe_browsing/password_protection/password_protection_reque
st.h" |
| 18 #include "components/safe_browsing_db/database_manager.h" | 18 #include "components/safe_browsing_db/database_manager.h" |
| 19 #include "components/safe_browsing_db/v4_protocol_manager_util.h" | 19 #include "components/safe_browsing_db/v4_protocol_manager_util.h" |
| 20 #include "content/public/browser/browser_thread.h" | 20 #include "content/public/browser/browser_thread.h" |
| 21 #include "content/public/browser/web_contents.h" |
| 21 #include "google_apis/google_api_keys.h" | 22 #include "google_apis/google_api_keys.h" |
| 22 #include "net/base/escape.h" | 23 #include "net/base/escape.h" |
| 24 #include "net/base/url_util.h" |
| 23 | 25 |
| 24 using content::BrowserThread; | 26 using content::BrowserThread; |
| 27 using content::WebContents; |
| 25 using history::HistoryService; | 28 using history::HistoryService; |
| 26 | 29 |
| 27 namespace safe_browsing { | 30 namespace safe_browsing { |
| 28 | 31 |
| 29 namespace { | 32 namespace { |
| 30 | 33 |
| 31 // Keys for storing password protection verdict into a DictionaryValue. | 34 // Keys for storing password protection verdict into a DictionaryValue. |
| 32 const char kCacheCreationTime[] = "cache_creation_time"; | 35 const char kCacheCreationTime[] = "cache_creation_time"; |
| 33 const char kVerdictProto[] = "verdict_proto"; | 36 const char kVerdictProto[] = "verdict_proto"; |
| 34 const int kRequestTimeoutMs = 10000; | 37 const int kRequestTimeoutMs = 10000; |
| (...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 99 } | 102 } |
| 100 | 103 |
| 101 void PasswordProtectionService::CheckCsdWhitelistOnIOThread( | 104 void PasswordProtectionService::CheckCsdWhitelistOnIOThread( |
| 102 const GURL& url, | 105 const GURL& url, |
| 103 bool* check_result) { | 106 bool* check_result) { |
| 104 DCHECK_CURRENTLY_ON(BrowserThread::IO); | 107 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| 105 *check_result = | 108 *check_result = |
| 106 url.is_valid() ? database_manager_->MatchCsdWhitelistUrl(url) : true; | 109 url.is_valid() ? database_manager_->MatchCsdWhitelistUrl(url) : true; |
| 107 } | 110 } |
| 108 | 111 |
| 112 bool PasswordProtectionService::CanGetReputationOfURL(const GURL& url) { |
| 113 if (!url.is_valid() || !url.SchemeIsHTTPOrHTTPS()) |
| 114 return false; |
| 115 |
| 116 const std::string& hostname = url.HostNoBrackets(); |
| 117 return !net::IsLocalhost(hostname) && !net::IsHostnameNonUnique(hostname) && |
| 118 hostname.find('.') != std::string::npos; |
| 119 } |
| 120 |
| 109 LoginReputationClientResponse::VerdictType | 121 LoginReputationClientResponse::VerdictType |
| 110 PasswordProtectionService::GetCachedVerdict( | 122 PasswordProtectionService::GetCachedVerdict( |
| 111 const GURL& url, | 123 const GURL& url, |
| 112 LoginReputationClientResponse* out_response) { | 124 LoginReputationClientResponse* out_response) { |
| 113 if (!url.is_valid()) | 125 if (!url.is_valid()) |
| 114 return LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED; | 126 return LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED; |
| 115 | 127 |
| 116 DCHECK(content_settings_); | 128 DCHECK(content_settings_); |
| 117 | 129 |
| 118 GURL hostname = GetHostNameWithHTTPScheme(url); | 130 GURL hostname = GetHostNameWithHTTPScheme(url); |
| (...skipping 125 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 244 // |verdict_diectionary|. | 256 // |verdict_diectionary|. |
| 245 content_settings_->SetWebsiteSettingDefaultScope( | 257 content_settings_->SetWebsiteSettingDefaultScope( |
| 246 primary_pattern_url, GURL(), | 258 primary_pattern_url, GURL(), |
| 247 CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION, std::string(), | 259 CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION, std::string(), |
| 248 std::move(verdict_dictionary)); | 260 std::move(verdict_dictionary)); |
| 249 } | 261 } |
| 250 } | 262 } |
| 251 } | 263 } |
| 252 | 264 |
| 253 void PasswordProtectionService::StartRequest( | 265 void PasswordProtectionService::StartRequest( |
| 266 WebContents* web_contents, |
| 254 const GURL& main_frame_url, | 267 const GURL& main_frame_url, |
| 255 const GURL& password_form_action, | 268 const GURL& password_form_action, |
| 256 const GURL& password_form_frame_url, | 269 const GURL& password_form_frame_url, |
| 257 const std::string& saved_domain, | 270 const std::string& saved_domain, |
| 258 LoginReputationClientRequest::TriggerType type) { | 271 LoginReputationClientRequest::TriggerType type) { |
| 259 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 272 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 260 scoped_refptr<PasswordProtectionRequest> request( | 273 scoped_refptr<PasswordProtectionRequest> request( |
| 261 new PasswordProtectionRequest(main_frame_url, password_form_action, | 274 new PasswordProtectionRequest(web_contents, main_frame_url, |
| 275 password_form_action, |
| 262 password_form_frame_url, saved_domain, type, | 276 password_form_frame_url, saved_domain, type, |
| 263 this, GetRequestTimeoutInMS())); | 277 this, GetRequestTimeoutInMS())); |
| 264 DCHECK(request); | 278 DCHECK(request); |
| 265 request->Start(); | 279 request->Start(); |
| 266 requests_.insert(std::move(request)); | 280 requests_.insert(std::move(request)); |
| 267 } | 281 } |
| 268 | 282 |
| 269 void PasswordProtectionService::MaybeStartPasswordFieldOnFocusRequest( | 283 void PasswordProtectionService::MaybeStartPasswordFieldOnFocusRequest( |
| 284 WebContents* web_contents, |
| 270 const GURL& main_frame_url, | 285 const GURL& main_frame_url, |
| 271 const GURL& password_form_action, | 286 const GURL& password_form_action, |
| 272 const GURL& password_form_frame_url) { | 287 const GURL& password_form_frame_url) { |
| 273 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 288 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 274 RequestOutcome request_outcome; | 289 if (CanSendPing(kPasswordFieldOnFocusPinging, main_frame_url)) { |
| 275 if (!IsPingingEnabled(kPasswordFieldOnFocusPinging, &request_outcome)) { | 290 StartRequest(web_contents, main_frame_url, password_form_action, |
| 276 RecordPingingDisabledReason(kPasswordFieldOnFocusPinging, request_outcome); | 291 password_form_frame_url, |
| 277 return; | 292 std::string(), /* saved_domain: not used for this type */ |
| 293 LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE); |
| 278 } | 294 } |
| 279 | |
| 280 // Skip URLs that we can't get a reliable reputation for. | |
| 281 if (!main_frame_url.is_valid() || !main_frame_url.SchemeIsHTTPOrHTTPS()) { | |
| 282 return; | |
| 283 } | |
| 284 | |
| 285 StartRequest(main_frame_url, password_form_action, password_form_frame_url, | |
| 286 std::string(), /* saved_domain: not used for this type */ | |
| 287 LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE); | |
| 288 } | 295 } |
| 289 | 296 |
| 290 void PasswordProtectionService::MaybeStartProtectedPasswordEntryRequest( | 297 void PasswordProtectionService::MaybeStartProtectedPasswordEntryRequest( |
| 298 WebContents* web_contents, |
| 291 const GURL& main_frame_url, | 299 const GURL& main_frame_url, |
| 292 const std::string& saved_domain) { | 300 const std::string& saved_domain) { |
| 293 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 301 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 294 RequestOutcome request_outcome; | 302 if (CanSendPing(kProtectedPasswordEntryPinging, main_frame_url)) { |
| 295 if (!IsPingingEnabled(kProtectedPasswordEntryPinging, &request_outcome)) { | 303 StartRequest(web_contents, main_frame_url, GURL(), GURL(), saved_domain, |
| 296 RecordPingingDisabledReason(kProtectedPasswordEntryPinging, | 304 LoginReputationClientRequest::PASSWORD_REUSE_EVENT); |
| 297 request_outcome); | |
| 298 return; | |
| 299 } | 305 } |
| 306 } |
| 300 | 307 |
| 301 // Skip URLs that we can't get a reliable reputation for. | 308 bool PasswordProtectionService::CanSendPing(const base::Feature& feature, |
| 302 if (!main_frame_url.is_valid() || !main_frame_url.SchemeIsHTTPOrHTTPS()) { | 309 const GURL& main_frame_url) { |
| 303 return; | 310 RequestOutcome request_outcome = URL_NOT_VALID_FOR_REPUTATION_COMPUTING; |
| 311 if (IsPingingEnabled(kPasswordFieldOnFocusPinging, &request_outcome) && |
| 312 CanGetReputationOfURL(main_frame_url)) { |
| 313 return true; |
| 304 } | 314 } |
| 305 | 315 RecordNoPingingReason(feature, request_outcome); |
| 306 StartRequest(main_frame_url, GURL(), GURL(), saved_domain, | 316 return false; |
| 307 LoginReputationClientRequest::PASSWORD_REUSE_EVENT); | |
| 308 } | 317 } |
| 309 | 318 |
| 310 void PasswordProtectionService::RequestFinished( | 319 void PasswordProtectionService::RequestFinished( |
| 311 PasswordProtectionRequest* request, | 320 PasswordProtectionRequest* request, |
| 312 std::unique_ptr<LoginReputationClientResponse> response) { | 321 std::unique_ptr<LoginReputationClientResponse> response) { |
| 313 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 322 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 314 | 323 |
| 315 DCHECK(request); | 324 DCHECK(request); |
| 316 // TODO(jialiul): We don't cache verdict for incognito mode for now. | 325 if (response) |
| 317 // Later we may consider temporarily caching verdict. | |
| 318 if (response && !IsIncognito()) | |
| 319 CacheVerdict(request->main_frame_url(), response.get(), base::Time::Now()); | 326 CacheVerdict(request->main_frame_url(), response.get(), base::Time::Now()); |
| 320 | 327 |
| 321 // Finished processing this request. Remove it from pending list. | 328 // Finished processing this request. Remove it from pending list. |
| 322 for (auto it = requests_.begin(); it != requests_.end(); it++) { | 329 for (auto it = requests_.begin(); it != requests_.end(); it++) { |
| 323 if (it->get() == request) { | 330 if (it->get() == request) { |
| 324 requests_.erase(it); | 331 requests_.erase(it); |
| 325 break; | 332 break; |
| 326 } | 333 } |
| 327 } | 334 } |
| 328 } | 335 } |
| (...skipping 218 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 547 const std::string serialized_proto(verdict->SerializeAsString()); | 554 const std::string serialized_proto(verdict->SerializeAsString()); |
| 548 const std::vector<char> verdict_blob(serialized_proto.begin(), | 555 const std::vector<char> verdict_blob(serialized_proto.begin(), |
| 549 serialized_proto.end()); | 556 serialized_proto.end()); |
| 550 std::unique_ptr<base::Value> binary_value = | 557 std::unique_ptr<base::Value> binary_value = |
| 551 base::MakeUnique<base::Value>(verdict_blob); | 558 base::MakeUnique<base::Value>(verdict_blob); |
| 552 DCHECK_EQ(base::Value::Type::BINARY, binary_value->type()); | 559 DCHECK_EQ(base::Value::Type::BINARY, binary_value->type()); |
| 553 result->Set(kVerdictProto, std::move(binary_value)); | 560 result->Set(kVerdictProto, std::move(binary_value)); |
| 554 return result; | 561 return result; |
| 555 } | 562 } |
| 556 | 563 |
| 557 void PasswordProtectionService::RecordPingingDisabledReason( | 564 void PasswordProtectionService::RecordNoPingingReason( |
| 558 const base::Feature& feature, | 565 const base::Feature& feature, |
| 559 RequestOutcome reason) { | 566 RequestOutcome reason) { |
| 560 DCHECK(feature.name == kProtectedPasswordEntryPinging.name || | 567 DCHECK(feature.name == kProtectedPasswordEntryPinging.name || |
| 561 feature.name == kPasswordFieldOnFocusPinging.name); | 568 feature.name == kPasswordFieldOnFocusPinging.name); |
| 562 | 569 |
| 563 bool is_password_entry_ping = | 570 bool is_password_entry_ping = |
| 564 feature.name == kProtectedPasswordEntryPinging.name; | 571 feature.name == kProtectedPasswordEntryPinging.name; |
| 565 | 572 |
| 566 if (is_password_entry_ping) { | 573 if (is_password_entry_ping) { |
| 567 UMA_HISTOGRAM_ENUMERATION(kPasswordEntryRequestOutcomeHistogramName, reason, | 574 UMA_HISTOGRAM_ENUMERATION(kPasswordEntryRequestOutcomeHistogramName, reason, |
| 568 MAX_OUTCOME); | 575 MAX_OUTCOME); |
| 569 } else { | 576 } else { |
| 570 UMA_HISTOGRAM_ENUMERATION(kPasswordOnFocusRequestOutcomeHistogramName, | 577 UMA_HISTOGRAM_ENUMERATION(kPasswordOnFocusRequestOutcomeHistogramName, |
| 571 reason, MAX_OUTCOME); | 578 reason, MAX_OUTCOME); |
| 572 } | 579 } |
| 573 } | 580 } |
| 574 | 581 |
| 575 } // namespace safe_browsing | 582 } // namespace safe_browsing |
| OLD | NEW |