| OLD | NEW | 
|---|
|  | (Empty) | 
| 1 // Copyright (c) 2017 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 |  | 
| 5 #include "components/safe_browsing/base_resource_throttle.h" |  | 
| 6 |  | 
| 7 #include "base/metrics/histogram_macros.h" |  | 
| 8 #include "base/trace_event/trace_event.h" |  | 
| 9 #include "base/values.h" |  | 
| 10 #include "components/safe_browsing/base_ui_manager.h" |  | 
| 11 #include "components/safe_browsing_db/util.h" |  | 
| 12 #include "components/security_interstitials/content/unsafe_resource.h" |  | 
| 13 #include "content/public/browser/browser_thread.h" |  | 
| 14 #include "content/public/browser/resource_request_info.h" |  | 
| 15 #include "content/public/browser/web_contents.h" |  | 
| 16 #include "net/base/load_flags.h" |  | 
| 17 #include "net/log/net_log_capture_mode.h" |  | 
| 18 #include "net/log/net_log_source.h" |  | 
| 19 #include "net/log/net_log_source_type.h" |  | 
| 20 #include "net/url_request/redirect_info.h" |  | 
| 21 #include "net/url_request/url_request.h" |  | 
| 22 |  | 
| 23 using net::NetLogEventType; |  | 
| 24 using net::NetLogSourceType; |  | 
| 25 |  | 
| 26 namespace safe_browsing { |  | 
| 27 |  | 
| 28 namespace { |  | 
| 29 |  | 
| 30 // Maximum time in milliseconds to wait for the safe browsing service to |  | 
| 31 // verify a URL. After this amount of time the outstanding check will be |  | 
| 32 // aborted, and the URL will be treated as if it were safe. |  | 
| 33 const int kCheckUrlTimeoutMs = 5000; |  | 
| 34 |  | 
| 35 // Return a dictionary with "url"=|url-spec| and optionally |  | 
| 36 // |name|=|value| (if not null), for netlogging. |  | 
| 37 // This will also add a reference to the original request's net_log ID. |  | 
| 38 std::unique_ptr<base::Value> NetLogUrlCallback( |  | 
| 39     const net::URLRequest* request, |  | 
| 40     const GURL& url, |  | 
| 41     const char* name, |  | 
| 42     const char* value, |  | 
| 43     net::NetLogCaptureMode /* capture_mode */) { |  | 
| 44   std::unique_ptr<base::DictionaryValue> event_params( |  | 
| 45       new base::DictionaryValue()); |  | 
| 46   event_params->SetString("url", url.spec()); |  | 
| 47   if (name && value) |  | 
| 48     event_params->SetString(name, value); |  | 
| 49   request->net_log().source().AddToEventParameters(event_params.get()); |  | 
| 50   return std::move(event_params); |  | 
| 51 } |  | 
| 52 |  | 
| 53 // Return a dictionary with |name|=|value|, for netlogging. |  | 
| 54 std::unique_ptr<base::Value> NetLogStringCallback(const char* name, |  | 
| 55                                                   const char* value, |  | 
| 56                                                   net::NetLogCaptureMode) { |  | 
| 57   std::unique_ptr<base::DictionaryValue> event_params( |  | 
| 58       new base::DictionaryValue()); |  | 
| 59   if (name && value) |  | 
| 60     event_params->SetString(name, value); |  | 
| 61   return std::move(event_params); |  | 
| 62 } |  | 
| 63 |  | 
| 64 }  // namespace |  | 
| 65 |  | 
| 66 // TODO(eroman): Downgrade these CHECK()s to DCHECKs once there is more |  | 
| 67 //               unit test coverage. |  | 
| 68 |  | 
| 69 BaseResourceThrottle::BaseResourceThrottle( |  | 
| 70     const net::URLRequest* request, |  | 
| 71     content::ResourceType resource_type, |  | 
| 72     scoped_refptr<SafeBrowsingDatabaseManager> database_manager, |  | 
| 73     scoped_refptr<BaseUIManager> ui_manager) |  | 
| 74     : ui_manager_(ui_manager), |  | 
| 75       threat_type_(SB_THREAT_TYPE_SAFE), |  | 
| 76       database_manager_(database_manager), |  | 
| 77       request_(request), |  | 
| 78       state_(STATE_NONE), |  | 
| 79       defer_state_(DEFERRED_NONE), |  | 
| 80       resource_type_(resource_type), |  | 
| 81       net_log_with_source_( |  | 
| 82           net::NetLogWithSource::Make(request->net_log().net_log(), |  | 
| 83                                       NetLogSourceType::SAFE_BROWSING)) {} |  | 
| 84 |  | 
| 85 // static |  | 
| 86 BaseResourceThrottle* BaseResourceThrottle::MaybeCreate( |  | 
| 87     net::URLRequest* request, |  | 
| 88     content::ResourceType resource_type, |  | 
| 89     scoped_refptr<SafeBrowsingDatabaseManager> database_manager, |  | 
| 90     scoped_refptr<BaseUIManager> ui_manager) { |  | 
| 91   if (database_manager->IsSupported()) { |  | 
| 92     return new BaseResourceThrottle(request, resource_type, |  | 
| 93                                     database_manager, ui_manager); |  | 
| 94   } |  | 
| 95   return nullptr; |  | 
| 96 } |  | 
| 97 |  | 
| 98 BaseResourceThrottle::~BaseResourceThrottle() { |  | 
| 99   if (defer_state_ != DEFERRED_NONE) { |  | 
| 100     EndNetLogEvent(NetLogEventType::SAFE_BROWSING_DEFERRED, nullptr, nullptr); |  | 
| 101   } |  | 
| 102 |  | 
| 103   if (state_ == STATE_CHECKING_URL) { |  | 
| 104     database_manager_->CancelCheck(this); |  | 
| 105     EndNetLogEvent(NetLogEventType::SAFE_BROWSING_CHECKING_URL, "result", |  | 
| 106                    "request_canceled"); |  | 
| 107   } |  | 
| 108 } |  | 
| 109 |  | 
| 110 // Note on net_log calls: SAFE_BROWSING_DEFERRED events must be wholly |  | 
| 111 // nested within SAFE_BROWSING_CHECKING_URL events.  Synchronous checks |  | 
| 112 // are not logged at all. |  | 
| 113 void BaseResourceThrottle::BeginNetLogEvent(NetLogEventType type, |  | 
| 114                                             const GURL& url, |  | 
| 115                                             const char* name, |  | 
| 116                                             const char* value) { |  | 
| 117   net_log_with_source_.BeginEvent( |  | 
| 118       type, base::Bind(&NetLogUrlCallback, request_, url, name, value)); |  | 
| 119   request_->net_log().AddEvent( |  | 
| 120       type, net_log_with_source_.source().ToEventParametersCallback()); |  | 
| 121 } |  | 
| 122 |  | 
| 123 void BaseResourceThrottle::EndNetLogEvent(NetLogEventType type, |  | 
| 124                                           const char* name, |  | 
| 125                                           const char* value) { |  | 
| 126   net_log_with_source_.EndEvent(type, |  | 
| 127                                 base::Bind(&NetLogStringCallback, name, value)); |  | 
| 128   request_->net_log().AddEvent( |  | 
| 129       type, net_log_with_source_.source().ToEventParametersCallback()); |  | 
| 130 } |  | 
| 131 |  | 
| 132 void BaseResourceThrottle::WillStartRequest(bool* defer) { |  | 
| 133   // We need to check the new URL before starting the request. |  | 
| 134   if (CheckUrl(request_->url())) |  | 
| 135     return; |  | 
| 136 |  | 
| 137   // We let the check run in parallel with resource load only if this |  | 
| 138   // db_manager only supports asynchronous checks, like on mobile. |  | 
| 139   // Otherwise, we defer now. |  | 
| 140   if (database_manager_->ChecksAreAlwaysAsync()) |  | 
| 141     return; |  | 
| 142 |  | 
| 143   // If the URL couldn't be verified synchronously, defer starting the |  | 
| 144   // request until the check has completed. |  | 
| 145   defer_state_ = DEFERRED_START; |  | 
| 146   defer_start_time_ = base::TimeTicks::Now(); |  | 
| 147   *defer = true; |  | 
| 148   BeginNetLogEvent(NetLogEventType::SAFE_BROWSING_DEFERRED, request_->url(), |  | 
| 149                    "defer_reason", "at_start"); |  | 
| 150 } |  | 
| 151 |  | 
| 152 void BaseResourceThrottle::WillProcessResponse(bool* defer) { |  | 
| 153   CHECK_EQ(defer_state_, DEFERRED_NONE); |  | 
| 154   // TODO(nparker): Maybe remove this check, since it should have no effect. |  | 
| 155   if (!database_manager_->ChecksAreAlwaysAsync()) |  | 
| 156     return; |  | 
| 157 |  | 
| 158   if (state_ == STATE_CHECKING_URL || |  | 
| 159       state_ == STATE_DISPLAYING_BLOCKING_PAGE) { |  | 
| 160     defer_state_ = DEFERRED_PROCESSING; |  | 
| 161     defer_start_time_ = base::TimeTicks::Now(); |  | 
| 162     *defer = true; |  | 
| 163     BeginNetLogEvent(NetLogEventType::SAFE_BROWSING_DEFERRED, request_->url(), |  | 
| 164                      "defer_reason", "at_response"); |  | 
| 165   } |  | 
| 166 } |  | 
| 167 |  | 
| 168 bool BaseResourceThrottle::MustProcessResponseBeforeReadingBody() { |  | 
| 169   // On Android, SafeBrowsing may only decide to cancel the request when the |  | 
| 170   // response has been received. Therefore, no part of it should be cached |  | 
| 171   // until this ResourceThrottle has been able to check the response. This |  | 
| 172   // prevents the following scenario: |  | 
| 173   //   1) A request is made for foo.com which has been hacked. |  | 
| 174   //   2) The request is only canceled at WillProcessResponse stage, but part of |  | 
| 175   //   it has been cached. |  | 
| 176   //   3) foo.com is no longer hacked and removed from the SafeBrowsing list. |  | 
| 177   //   4) The user requests foo.com, which is not on the SafeBrowsing list. This |  | 
| 178   //   is deemed safe. However, the resource is actually served from cache, |  | 
| 179   //   using the version that was previously stored. |  | 
| 180   //   5) This results in the  user accessing an unsafe resource without being |  | 
| 181   //   notified that it's dangerous. |  | 
| 182   // TODO(clamy): Add a browser test that checks this specific scenario. |  | 
| 183   return true; |  | 
| 184 } |  | 
| 185 |  | 
| 186 void BaseResourceThrottle::WillRedirectRequest( |  | 
| 187     const net::RedirectInfo& redirect_info, |  | 
| 188     bool* defer) { |  | 
| 189   CHECK_EQ(defer_state_, DEFERRED_NONE); |  | 
| 190 |  | 
| 191   // Prev check completed and was safe. |  | 
| 192   if (state_ == STATE_NONE) { |  | 
| 193     // Save the redirect urls for possible malware detail reporting later. |  | 
| 194     redirect_urls_.push_back(redirect_info.new_url); |  | 
| 195 |  | 
| 196     // We need to check the new URL before following the redirect. |  | 
| 197     if (CheckUrl(redirect_info.new_url)) |  | 
| 198       return; |  | 
| 199     defer_state_ = DEFERRED_REDIRECT; |  | 
| 200   } else { |  | 
| 201     CHECK(state_ == STATE_CHECKING_URL || |  | 
| 202           state_ == STATE_DISPLAYING_BLOCKING_PAGE); |  | 
| 203     // We can't check this new URL until we have finished checking |  | 
| 204     // the prev one, or resumed from the blocking page. |  | 
| 205     unchecked_redirect_url_ = redirect_info.new_url; |  | 
| 206     defer_state_ = DEFERRED_UNCHECKED_REDIRECT; |  | 
| 207   } |  | 
| 208 |  | 
| 209   defer_start_time_ = base::TimeTicks::Now(); |  | 
| 210   *defer = true; |  | 
| 211   BeginNetLogEvent( |  | 
| 212       NetLogEventType::SAFE_BROWSING_DEFERRED, redirect_info.new_url, |  | 
| 213       "defer_reason", |  | 
| 214       defer_state_ == DEFERRED_REDIRECT ? "redirect" : "unchecked_redirect"); |  | 
| 215 } |  | 
| 216 |  | 
| 217 const char* BaseResourceThrottle::GetNameForLogging() const { |  | 
| 218   return "BaseResourceThrottle"; |  | 
| 219 } |  | 
| 220 |  | 
| 221 void BaseResourceThrottle::MaybeDestroyPrerenderContents( |  | 
| 222     const content::ResourceRequestInfo* info) {} |  | 
| 223 |  | 
| 224 // SafeBrowsingService::Client implementation, called on the IO thread once |  | 
| 225 // the URL has been classified. |  | 
| 226 void BaseResourceThrottle::OnCheckBrowseUrlResult( |  | 
| 227     const GURL& url, |  | 
| 228     SBThreatType threat_type, |  | 
| 229     const ThreatMetadata& metadata) { |  | 
| 230   CHECK_EQ(state_, STATE_CHECKING_URL); |  | 
| 231   CHECK(url.is_valid()); |  | 
| 232   CHECK(url_being_checked_.is_valid()); |  | 
| 233   CHECK_EQ(url, url_being_checked_); |  | 
| 234 |  | 
| 235   timer_.Stop();  // Cancel the timeout timer. |  | 
| 236   threat_type_ = threat_type; |  | 
| 237   state_ = STATE_NONE; |  | 
| 238 |  | 
| 239   if (defer_state_ != DEFERRED_NONE) { |  | 
| 240     EndNetLogEvent(NetLogEventType::SAFE_BROWSING_DEFERRED, nullptr, nullptr); |  | 
| 241   } |  | 
| 242   EndNetLogEvent( |  | 
| 243       NetLogEventType::SAFE_BROWSING_CHECKING_URL, "result", |  | 
| 244       threat_type_ == SB_THREAT_TYPE_SAFE ? "safe" : "unsafe"); |  | 
| 245 |  | 
| 246   if (threat_type == SB_THREAT_TYPE_SAFE) { |  | 
| 247     if (defer_state_ != DEFERRED_NONE) { |  | 
| 248       // Log how much time the safe browsing check cost us. |  | 
| 249       ui_manager_->LogPauseDelay(base::TimeTicks::Now() - defer_start_time_); |  | 
| 250       ResumeRequest(); |  | 
| 251     } else { |  | 
| 252       ui_manager_->LogPauseDelay(base::TimeDelta()); |  | 
| 253     } |  | 
| 254     return; |  | 
| 255   } |  | 
| 256 |  | 
| 257   const content::ResourceRequestInfo* info = |  | 
| 258       content::ResourceRequestInfo::ForRequest(request_); |  | 
| 259 |  | 
| 260   if (request_->load_flags() & net::LOAD_PREFETCH) { |  | 
| 261     // Destroy the prefetch with FINAL_STATUS_SAFEBROSWING. |  | 
| 262     if (resource_type_ == content::RESOURCE_TYPE_MAIN_FRAME) { |  | 
| 263       MaybeDestroyPrerenderContents(info); |  | 
| 264     } |  | 
| 265     // Don't prefetch resources that fail safe browsing, disallow them. |  | 
| 266     Cancel(); |  | 
| 267     UMA_HISTOGRAM_ENUMERATION("SB2.ResourceTypes2.UnsafePrefetchCanceled", |  | 
| 268                               resource_type_, content::RESOURCE_TYPE_LAST_TYPE); |  | 
| 269     return; |  | 
| 270   } |  | 
| 271 |  | 
| 272   UMA_HISTOGRAM_ENUMERATION("SB2.ResourceTypes2.Unsafe", resource_type_, |  | 
| 273                             content::RESOURCE_TYPE_LAST_TYPE); |  | 
| 274 |  | 
| 275   security_interstitials::UnsafeResource resource; |  | 
| 276   resource.url = url; |  | 
| 277   resource.original_url = request_->original_url(); |  | 
| 278   resource.redirect_urls = redirect_urls_; |  | 
| 279   resource.is_subresource = resource_type_ != content::RESOURCE_TYPE_MAIN_FRAME; |  | 
| 280   resource.is_subframe = resource_type_ == content::RESOURCE_TYPE_SUB_FRAME; |  | 
| 281   resource.threat_type = threat_type; |  | 
| 282   resource.threat_metadata = metadata; |  | 
| 283   resource.callback = base::Bind( |  | 
| 284       &BaseResourceThrottle::OnBlockingPageComplete, AsWeakPtr()); |  | 
| 285   resource.callback_thread = content::BrowserThread::GetTaskRunnerForThread( |  | 
| 286       content::BrowserThread::IO); |  | 
| 287   resource.web_contents_getter = info->GetWebContentsGetterForRequest(); |  | 
| 288   resource.threat_source = database_manager_->GetThreatSource(); |  | 
| 289 |  | 
| 290   state_ = STATE_DISPLAYING_BLOCKING_PAGE; |  | 
| 291 |  | 
| 292   StartDisplayingBlockingPageHelper(resource); |  | 
| 293 } |  | 
| 294 |  | 
| 295 void BaseResourceThrottle::StartDisplayingBlockingPageHelper( |  | 
| 296     security_interstitials::UnsafeResource resource) { |  | 
| 297   content::BrowserThread::PostTask( |  | 
| 298       content::BrowserThread::UI, FROM_HERE, |  | 
| 299       base::Bind(&BaseResourceThrottle::StartDisplayingBlockingPage, |  | 
| 300                  AsWeakPtr(), ui_manager_, resource)); |  | 
| 301 } |  | 
| 302 |  | 
| 303 // Static |  | 
| 304 void BaseResourceThrottle::StartDisplayingBlockingPage( |  | 
| 305     const base::WeakPtr<BaseResourceThrottle>& throttle, |  | 
| 306     scoped_refptr<BaseUIManager> ui_manager, |  | 
| 307     const security_interstitials::UnsafeResource& resource) { |  | 
| 308   content::WebContents* web_contents = resource.web_contents_getter.Run(); |  | 
| 309   if (web_contents) { |  | 
| 310     ui_manager->DisplayBlockingPage(resource); |  | 
| 311     return; |  | 
| 312   } |  | 
| 313 |  | 
| 314   // Tab is gone or it's being prerendered. |  | 
| 315   content::BrowserThread::PostTask( |  | 
| 316       content::BrowserThread::IO, FROM_HERE, |  | 
| 317       base::Bind(&BaseResourceThrottle::Cancel, throttle)); |  | 
| 318 } |  | 
| 319 |  | 
| 320 void BaseResourceThrottle::OnBlockingPageComplete(bool proceed) { |  | 
| 321   CHECK_EQ(state_, STATE_DISPLAYING_BLOCKING_PAGE); |  | 
| 322   state_ = STATE_NONE; |  | 
| 323 |  | 
| 324   if (proceed) { |  | 
| 325     threat_type_ = SB_THREAT_TYPE_SAFE; |  | 
| 326     if (defer_state_ != DEFERRED_NONE) { |  | 
| 327       ResumeRequest(); |  | 
| 328     } |  | 
| 329   } else { |  | 
| 330     CancelResourceLoad(); |  | 
| 331   } |  | 
| 332 } |  | 
| 333 |  | 
| 334 void BaseResourceThrottle::CancelResourceLoad() { |  | 
| 335   Cancel(); |  | 
| 336 } |  | 
| 337 |  | 
| 338 scoped_refptr<BaseUIManager> BaseResourceThrottle::ui_manager() { |  | 
| 339   return ui_manager_; |  | 
| 340 } |  | 
| 341 |  | 
| 342 bool BaseResourceThrottle::CheckUrl(const GURL& url) { |  | 
| 343   TRACE_EVENT1("loader", "BaseResourceThrottle::CheckUrl", "url", |  | 
| 344                url.spec()); |  | 
| 345   CHECK_EQ(state_, STATE_NONE); |  | 
| 346   // To reduce aggregate latency on mobile, check only the most dangerous |  | 
| 347   // resource types. |  | 
| 348   if (!database_manager_->CanCheckResourceType(resource_type_)) { |  | 
| 349     // TODO(vakh): Consider changing this metric to SafeBrowsing.V4ResourceType |  | 
| 350     // to be consistent with the other PVer4 metrics. |  | 
| 351     UMA_HISTOGRAM_ENUMERATION("SB2.ResourceTypes2.Skipped", resource_type_, |  | 
| 352                               content::RESOURCE_TYPE_LAST_TYPE); |  | 
| 353     return true; |  | 
| 354   } |  | 
| 355 |  | 
| 356   // TODO(vakh): Consider changing this metric to SafeBrowsing.V4ResourceType to |  | 
| 357   // be consistent with the other PVer4 metrics. |  | 
| 358   UMA_HISTOGRAM_ENUMERATION("SB2.ResourceTypes2.Checked", resource_type_, |  | 
| 359                             content::RESOURCE_TYPE_LAST_TYPE); |  | 
| 360 |  | 
| 361   if (database_manager_->CheckBrowseUrl(url, this)) { |  | 
| 362     threat_type_ = SB_THREAT_TYPE_SAFE; |  | 
| 363     ui_manager_->LogPauseDelay(base::TimeDelta());  // No delay. |  | 
| 364     return true; |  | 
| 365   } |  | 
| 366 |  | 
| 367   state_ = STATE_CHECKING_URL; |  | 
| 368   url_being_checked_ = url; |  | 
| 369   BeginNetLogEvent(NetLogEventType::SAFE_BROWSING_CHECKING_URL, url, nullptr, |  | 
| 370                    nullptr); |  | 
| 371 |  | 
| 372   // Start a timer to abort the check if it takes too long. |  | 
| 373   // TODO(nparker): Set this only when we defer, based on remaining time, |  | 
| 374   // so we don't cancel earlier than necessary. |  | 
| 375   timer_.Start(FROM_HERE, base::TimeDelta::FromMilliseconds(kCheckUrlTimeoutMs), |  | 
| 376                this, &BaseResourceThrottle::OnCheckUrlTimeout); |  | 
| 377 |  | 
| 378   return false; |  | 
| 379 } |  | 
| 380 |  | 
| 381 void BaseResourceThrottle::OnCheckUrlTimeout() { |  | 
| 382   CHECK_EQ(state_, STATE_CHECKING_URL); |  | 
| 383 |  | 
| 384   database_manager_->CancelCheck(this); |  | 
| 385 |  | 
| 386   OnCheckBrowseUrlResult(url_being_checked_, safe_browsing::SB_THREAT_TYPE_SAFE, |  | 
| 387                          ThreatMetadata()); |  | 
| 388 } |  | 
| 389 |  | 
| 390 void BaseResourceThrottle::ResumeRequest() { |  | 
| 391   CHECK_EQ(state_, STATE_NONE); |  | 
| 392   CHECK_NE(defer_state_, DEFERRED_NONE); |  | 
| 393 |  | 
| 394   bool resume = true; |  | 
| 395   if (defer_state_ == DEFERRED_UNCHECKED_REDIRECT) { |  | 
| 396     // Save the redirect urls for possible malware detail reporting later. |  | 
| 397     redirect_urls_.push_back(unchecked_redirect_url_); |  | 
| 398     if (!CheckUrl(unchecked_redirect_url_)) { |  | 
| 399       // We're now waiting for the unchecked_redirect_url_. |  | 
| 400       defer_state_ = DEFERRED_REDIRECT; |  | 
| 401       resume = false; |  | 
| 402       BeginNetLogEvent(NetLogEventType::SAFE_BROWSING_DEFERRED, |  | 
| 403                        unchecked_redirect_url_, "defer_reason", |  | 
| 404                        "resumed_redirect"); |  | 
| 405     } |  | 
| 406   } |  | 
| 407 |  | 
| 408   if (resume) { |  | 
| 409     defer_state_ = DEFERRED_NONE; |  | 
| 410     Resume(); |  | 
| 411   } |  | 
| 412 } |  | 
| 413 |  | 
| 414 }  // namespace safe_browsing |  | 
| OLD | NEW | 
|---|