 Chromium Code Reviews
 Chromium Code Reviews Issue 2900563002:
  Network service: Safe browsing check for sub-resources from renderer.  (Closed)
    
  
    Issue 2900563002:
  Network service: Safe browsing check for sub-resources from renderer.  (Closed) 
  | OLD | NEW | 
|---|---|
| (Empty) | |
| 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 | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "chrome/browser/safe_browsing/mojo_safe_browsing_impl.h" | |
| 6 | |
| 7 #include <vector> | |
| 8 | |
| 9 #include "base/memory/ptr_util.h" | |
| 10 #include "base/memory/weak_ptr.h" | |
| 11 #include "base/timer/timer.h" | |
| 12 #include "chrome/browser/browser_process.h" | |
| 13 #include "chrome/browser/prerender/prerender_contents.h" | |
| 14 #include "chrome/browser/safe_browsing/ui_manager.h" | |
| 15 #include "components/safe_browsing_db/database_manager.h" | |
| 16 #include "content/public/browser/browser_thread.h" | |
| 17 #include "content/public/browser/render_frame_host.h" | |
| 18 #include "content/public/browser/web_contents.h" | |
| 19 #include "content/public/common/resource_type.h" | |
| 20 #include "mojo/public/cpp/bindings/strong_binding.h" | |
| 21 #include "net/base/load_flags.h" | |
| 22 | |
| 23 namespace safe_browsing { | |
| 24 namespace { | |
| 25 | |
| 26 // TODO(yzshen): Share such value with safe_browsing::BaseResourceThrottle. | |
| 27 // Maximum time in milliseconds to wait for the SafeBrowsing service reputation | |
| 28 // check. After this amount of time the outstanding check will be aborted, and | |
| 29 // the resource will be treated as if it were safe. | |
| 30 const int kCheckUrlTimeoutMs = 5000; | |
| 31 | |
| 32 content::WebContents* GetWebContentsFromID(int render_process_id, | |
| 33 int render_frame_id) { | |
| 34 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | |
| 35 content::RenderFrameHost* render_frame_host = | |
| 36 content::RenderFrameHost::FromID(render_process_id, render_frame_id); | |
| 37 if (!render_frame_host) | |
| 38 return nullptr; | |
| 39 | |
| 40 return content::WebContents::FromRenderFrameHost(render_frame_host); | |
| 41 } | |
| 42 | |
| 43 // TODO(yzshen): Handle the case where SafeBrowsing is not enabled, or | |
| 44 // !database_manager()->IsSupported(). | |
| 45 // TODO(yzshen): Make sure it also works on Andorid. | |
| 46 // TODO(yzshen): Do all the logging like what BaseResourceThrottle does. | |
| 47 class SafeBrowsingUrlCheckerImpl : public chrome::mojom::SafeBrowsingUrlChecker, | |
| 48 public SafeBrowsingDatabaseManager::Client { | |
| 49 public: | |
| 50 SafeBrowsingUrlCheckerImpl( | |
| 51 int load_flags, | |
| 52 content::ResourceType resource_type, | |
| 53 scoped_refptr<SafeBrowsingDatabaseManager> database_manager, | |
| 54 scoped_refptr<SafeBrowsingUIManager> ui_manager, | |
| 55 int render_process_id, | |
| 56 int render_frame_id) | |
| 57 : load_flags_(load_flags), | |
| 58 resource_type_(resource_type), | |
| 59 render_process_id_(render_process_id), | |
| 60 render_frame_id_(render_frame_id), | |
| 61 database_manager_(std::move(database_manager)), | |
| 62 ui_manager_(std::move(ui_manager)), | |
| 63 weak_factory_(this) { | |
| 64 DCHECK_NE(resource_type, content::RESOURCE_TYPE_MAIN_FRAME); | |
| 65 DCHECK_NE(resource_type, content::RESOURCE_TYPE_SUB_FRAME); | |
| 66 } | |
| 67 | |
| 68 ~SafeBrowsingUrlCheckerImpl() override { | |
| 69 DCHECK_CURRENTLY_ON(content::BrowserThread::IO); | |
| 70 | |
| 71 if (state_ == STATE_CHECKING_URL) | |
| 72 database_manager_->CancelCheck(this); | |
| 73 | |
| 74 for (size_t i = next_index_; i < callbacks_.size(); ++i) | |
| 75 std::move(callbacks_[i]).Run(false); | |
| 76 } | |
| 77 | |
| 78 // chrome::mojom::SafeBrowsingUrlChecker implementation. | |
| 79 void CheckUrl(const GURL& url, CheckUrlCallback callback) override { | |
| 80 DCHECK_CURRENTLY_ON(content::BrowserThread::IO); | |
| 81 | |
| 82 DVLOG(1) << "SafeBrowsingUrlCheckerImpl checks URL: " << url; | |
| 83 urls_.push_back(url); | |
| 84 callbacks_.push_back(std::move(callback)); | |
| 85 | |
| 86 ProcessUrls(); | |
| 87 } | |
| 88 | |
| 89 private: | |
| 90 // SafeBrowsingDatabaseManager::Client implementation: | |
| 91 void OnCheckBrowseUrlResult(const GURL& url, | |
| 92 SBThreatType threat_type, | |
| 93 const ThreatMetadata& metadata) override { | |
| 94 DCHECK_EQ(STATE_CHECKING_URL, state_); | |
| 95 DCHECK_LT(next_index_, urls_.size()); | |
| 96 DCHECK_EQ(urls_[next_index_], url); | |
| 97 | |
| 98 timer_.Stop(); | |
| 99 if (threat_type == SB_THREAT_TYPE_SAFE) { | |
| 100 state_ = STATE_NONE; | |
| 101 std::move(callbacks_[next_index_]).Run(true); | |
| 102 next_index_++; | |
| 103 ProcessUrls(); | |
| 104 return; | |
| 105 } | |
| 106 | |
| 107 if (load_flags_ & net::LOAD_PREFETCH) { | |
| 108 BlockAndProcessUrls(); | |
| 109 return; | |
| 110 } | |
| 111 | |
| 112 security_interstitials::UnsafeResource resource; | |
| 113 resource.url = url; | |
| 114 resource.original_url = urls_[0]; | |
| 115 if (urls_.size() > 1) | |
| 116 resource.redirect_urls = | |
| 117 std::vector<GURL>(urls_.begin() + 1, urls_.end()); | |
| 118 resource.is_subresource = true; | |
| 119 resource.is_subframe = false; | |
| 120 resource.threat_type = threat_type; | |
| 121 resource.threat_metadata = metadata; | |
| 122 resource.callback = | |
| 123 base::Bind(&SafeBrowsingUrlCheckerImpl::OnBlockingPageComplete, | |
| 124 weak_factory_.GetWeakPtr()); | |
| 125 resource.callback_thread = content::BrowserThread::GetTaskRunnerForThread( | |
| 126 content::BrowserThread::IO); | |
| 127 resource.web_contents_getter = | |
| 128 base::Bind(&GetWebContentsFromID, render_process_id_, render_frame_id_); | |
| 129 resource.threat_source = database_manager_->GetThreatSource(); | |
| 130 | |
| 131 state_ = STATE_DISPLAYING_BLOCKING_PAGE; | |
| 132 | |
| 133 content::BrowserThread::PostTask( | |
| 134 content::BrowserThread::UI, FROM_HERE, | |
| 135 base::Bind(&SafeBrowsingUrlCheckerImpl::StartDisplayingBlockingPage, | |
| 136 weak_factory_.GetWeakPtr(), ui_manager_, resource)); | |
| 137 } | |
| 138 | |
| 139 static void StartDisplayingBlockingPage( | |
| 140 const base::WeakPtr<SafeBrowsingUrlCheckerImpl>& checker, | |
| 141 scoped_refptr<BaseUIManager> ui_manager, | |
| 142 const security_interstitials::UnsafeResource& resource) { | |
| 143 content::WebContents* web_contents = resource.web_contents_getter.Run(); | |
| 144 if (web_contents) { | |
| 145 prerender::PrerenderContents* prerender_contents = | |
| 146 prerender::PrerenderContents::FromWebContents(web_contents); | |
| 147 if (prerender_contents) { | |
| 148 prerender_contents->Destroy(prerender::FINAL_STATUS_SAFE_BROWSING); | |
| 149 } else { | |
| 150 ui_manager->DisplayBlockingPage(resource); | |
| 151 return; | |
| 152 } | |
| 153 } | |
| 154 | |
| 155 // Tab is gone or it's being prerendered. | |
| 156 content::BrowserThread::PostTask( | |
| 157 content::BrowserThread::IO, FROM_HERE, | |
| 158 base::BindOnce(&SafeBrowsingUrlCheckerImpl::BlockAndProcessUrls, | |
| 159 checker)); | |
| 160 } | |
| 161 | |
| 162 void OnCheckUrlTimeout() { | |
| 163 database_manager_->CancelCheck(this); | |
| 164 | |
| 165 OnCheckBrowseUrlResult(urls_[next_index_], | |
| 166 safe_browsing::SB_THREAT_TYPE_SAFE, | |
| 167 ThreatMetadata()); | |
| 168 } | |
| 169 | |
| 170 void ProcessUrls() { | |
| 171 DCHECK_NE(STATE_BLOCKED, state_); | |
| 172 | |
| 173 if (state_ == STATE_CHECKING_URL || | |
| 174 state_ == STATE_DISPLAYING_BLOCKING_PAGE) { | |
| 175 return; | |
| 176 } | |
| 177 | |
| 178 while (next_index_ < urls_.size()) { | |
| 179 DCHECK_EQ(STATE_NONE, state_); | |
| 180 // TODO(yzshen): Consider moving CanCheckResourceType() to the renderer | |
| 181 // side. That would save some IPCs. It requires a method on the | |
| 182 // SafeBrowsing mojo interface to query all supported resource types. | |
| 183 if (!database_manager_->CanCheckResourceType(resource_type_) || | |
| 184 database_manager_->CheckBrowseUrl(urls_[next_index_], this)) { | |
| 185 std::move(callbacks_[next_index_]).Run(true); | |
| 186 next_index_++; | |
| 187 continue; | |
| 188 } | |
| 189 | |
| 190 state_ = STATE_CHECKING_URL; | |
| 191 // Start a timer to abort the check if it takes too long. | |
| 192 timer_.Start(FROM_HERE, | |
| 193 base::TimeDelta::FromMilliseconds(kCheckUrlTimeoutMs), this, | |
| 194 &SafeBrowsingUrlCheckerImpl::OnCheckUrlTimeout); | |
| 195 | |
| 196 break; | |
| 197 } | |
| 198 } | |
| 199 | |
| 200 void BlockAndProcessUrls() { | |
| 201 DVLOG(1) << "SafeBrowsingUrlCheckerImpl blocks URL: " << urls_[next_index_]; | |
| 202 state_ = STATE_BLOCKED; | |
| 203 | |
| 204 // If user decided to not proceed through a warning, mark all the remaining | |
| 205 // redirects as "bad". | |
| 206 for (; next_index_ < callbacks_.size(); ++next_index_) | |
| 207 std::move(callbacks_[next_index_]).Run(false); | |
| 208 } | |
| 209 | |
| 210 void OnBlockingPageComplete(bool proceed) { | |
| 211 DCHECK_EQ(STATE_DISPLAYING_BLOCKING_PAGE, state_); | |
| 212 | |
| 213 if (proceed) { | |
| 214 state_ = STATE_NONE; | |
| 215 std::move(callbacks_[next_index_]).Run(true); | |
| 216 next_index_++; | |
| 217 ProcessUrls(); | |
| 218 } else { | |
| 219 BlockAndProcessUrls(); | |
| 220 } | |
| 221 } | |
| 222 | |
| 223 enum State { | |
| 224 // Haven't started checking or checking is complete. | |
| 225 STATE_NONE, | |
| 226 // We have one outstanding URL-check. | |
| 227 STATE_CHECKING_URL, | |
| 228 // We're displaying a blocking page. | |
| 229 STATE_DISPLAYING_BLOCKING_PAGE, | |
| 230 // The blocking page has returned *not* to proceed. | |
| 231 STATE_BLOCKED | |
| 232 }; | |
| 233 | |
| 234 const int load_flags_; | |
| 235 const content::ResourceType resource_type_; | |
| 236 const int render_process_id_; | |
| 237 const int render_frame_id_; | |
| 238 scoped_refptr<SafeBrowsingDatabaseManager> database_manager_; | |
| 239 scoped_refptr<BaseUIManager> ui_manager_; | |
| 240 | |
| 241 // The redirect chain for this resource, including the original URL and | |
| 242 // subsequent redirect URLs. | |
| 243 std::vector<GURL> urls_; | |
| 244 // Callbacks corresponding to |urls_| to report check results. |urls_| and | |
| 245 // |callbacks_| are always of the same size. | |
| 246 std::vector<CheckUrlCallback> callbacks_; | |
| 247 // |urls_| before |next_index_| have been checked. If |next_index_| is smaller | |
| 248 // than the size of |urls_|, the URL at |next_index_| is being processed. | |
| 249 size_t next_index_ = 0; | |
| 250 | |
| 251 State state_ = STATE_NONE; | |
| 252 | |
| 253 // Timer to abort the SafeBrowsing check if it takes too long. | |
| 254 base::OneShotTimer timer_; | |
| 255 | |
| 256 base::WeakPtrFactory<SafeBrowsingUrlCheckerImpl> weak_factory_; | |
| 257 | |
| 258 DISALLOW_COPY_AND_ASSIGN(SafeBrowsingUrlCheckerImpl); | |
| 259 }; | |
| 260 | |
| 261 } // namespace | |
| 262 | |
| 263 MojoSafeBrowsingImpl::MojoSafeBrowsingImpl( | |
| 264 scoped_refptr<SafeBrowsingDatabaseManager> database_manager, | |
| 265 scoped_refptr<SafeBrowsingUIManager> ui_manager, | |
| 266 int render_process_id) | |
| 267 : database_manager_(std::move(database_manager)), | |
| 268 ui_manager_(std::move(ui_manager)), | |
| 269 render_process_id_(render_process_id) {} | |
| 270 | |
| 271 MojoSafeBrowsingImpl::~MojoSafeBrowsingImpl() { | |
| 272 DCHECK_CURRENTLY_ON(content::BrowserThread::IO); | |
| 273 } | |
| 274 | |
| 275 // static | |
| 276 void MojoSafeBrowsingImpl::Create( | |
| 277 scoped_refptr<SafeBrowsingDatabaseManager> database_manager, | |
| 278 scoped_refptr<SafeBrowsingUIManager> ui_manager, | |
| 279 int render_process_id, | |
| 280 const service_manager::BindSourceInfo& source_info, | |
| 281 chrome::mojom::SafeBrowsingRequest request) { | |
| 282 DCHECK_CURRENTLY_ON(content::BrowserThread::IO); | |
| 283 mojo::MakeStrongBinding(base::MakeUnique<MojoSafeBrowsingImpl>( | |
| 284 std::move(database_manager), | |
| 285 std::move(ui_manager), render_process_id), | |
| 286 std::move(request)); | |
| 287 } | |
| 288 | |
| 289 void MojoSafeBrowsingImpl::CreateCheckerAndCheck( | |
| 290 int32_t render_frame_id, | |
| 291 chrome::mojom::SafeBrowsingUrlCheckerRequest request, | |
| 292 const GURL& url, | |
| 293 int32_t load_flags, | |
| 
Nathan Parker
2017/06/01 18:22:04
load_flags is int32_t here, and int above.  Want t
 
yzshen1
2017/06/01 18:36:52
Yeah. That is a good question.
I used it as int32_
 
Nathan Parker
2017/06/01 18:38:49
I assume all our platforms have 32-bit "ints" so i
 
yzshen1
2017/06/01 18:44:44
Done. I added static_cast to be explicit. Thanks!
 | |
| 294 content::ResourceType resource_type, | |
| 295 CreateCheckerAndCheckCallback callback) { | |
| 296 DCHECK_CURRENTLY_ON(content::BrowserThread::IO); | |
| 297 auto checker_impl = base::MakeUnique<SafeBrowsingUrlCheckerImpl>( | |
| 298 load_flags, resource_type, database_manager_, ui_manager_, | |
| 299 render_process_id_, render_frame_id); | |
| 300 checker_impl->CheckUrl(url, std::move(callback)); | |
| 301 mojo::MakeStrongBinding(std::move(checker_impl), std::move(request)); | |
| 302 } | |
| 303 | |
| 304 } // namespace safe_browsing | |
| OLD | NEW |