| // Copyright (c) 2017 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "components/safe_browsing/ping_manager.h" |
| |
| #include <utility> |
| |
| #include "base/bind.h" |
| #include "base/logging.h" |
| #include "base/memory/ptr_util.h" |
| #include "base/strings/string_util.h" |
| #include "base/strings/stringprintf.h" |
| #include "components/safe_browsing/db/v4_protocol_manager_util.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "google_apis/google_api_keys.h" |
| #include "net/base/escape.h" |
| #include "net/base/load_flags.h" |
| #include "net/traffic_annotation/network_traffic_annotation.h" |
| #include "net/url_request/url_fetcher.h" |
| #include "net/url_request/url_request_context.h" |
| #include "net/url_request/url_request_context_getter.h" |
| #include "net/url_request/url_request_status.h" |
| #include "services/network/public/cpp/simple_url_loader.h" |
| #include "url/gurl.h" |
| |
| using content::BrowserThread; |
| |
| namespace { |
| |
| const net::NetworkTrafficAnnotationTag kTrafficAnnotation = |
| net::DefineNetworkTrafficAnnotation("safe_browsing_extended_reporting", |
| R"( |
| semantics { |
| sender: "Safe Browsing Extended Reporting" |
| description: |
| "When a user is opted in to automatically reporting 'possible " |
| "security incidents to Google,' and they reach a bad page that's " |
| "flagged by Safe Browsing, Chrome will send a report to Google " |
| "with information about the threat. This helps Safe Browsing learn " |
| "where threats originate and thus protect more users." |
| trigger: |
| "When a red interstitial is shown, and the user is opted-in." |
| data: |
| "The report includes the URL and referrer chain of the page. If the " |
| "warning is triggered by a subresource on a partially loaded page, " |
| "the report will include the URL and referrer chain of sub frames " |
| "and resources loaded into the page. It may also include a subset " |
| "of headers for resources loaded, and some Google ad identifiers to " |
| "help block malicious ads." |
| destination: GOOGLE_OWNED_SERVICE |
| } |
| policy { |
| cookies_allowed: YES |
| cookies_store: "Safe Browsing Cookie Store" |
| setting: |
| "Users can control this feature via the 'Automatically report " |
| "details of possible security incidents to Google' setting under " |
| "'Privacy'. The feature is disabled by default." |
| chrome_policy { |
| SafeBrowsingExtendedReportingOptInAllowed { |
| policy_options {mode: MANDATORY} |
| SafeBrowsingExtendedReportingOptInAllowed: false |
| } |
| } |
| })"); |
| |
| } // namespace |
| |
| namespace safe_browsing { |
| |
| // SafeBrowsingPingManager implementation ---------------------------------- |
| |
| // static |
| std::unique_ptr<PingManager> PingManager::Create( |
| scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory, |
| const V4ProtocolConfig& config) { |
| return base::WrapUnique(new PingManager(url_loader_factory, config)); |
| } |
| |
| PingManager::PingManager( |
| scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory, |
| const V4ProtocolConfig& config) |
| : config_(config), url_loader_factory_(url_loader_factory) {} |
| |
| PingManager::~PingManager() {} |
| |
| // net::URLFetcherDelegate implementation ---------------------------------- |
| |
| // All SafeBrowsing request responses are handled here. |
| void PingManager::OnURLLoaderComplete( |
| network::SimpleURLLoader* source, |
| std::unique_ptr<std::string> response_body) { |
| auto it = safebrowsing_reports_.find(source); |
| DCHECK(it != safebrowsing_reports_.end()); |
| safebrowsing_reports_.erase(it); |
| } |
| |
| // Sends a SafeBrowsing "hit" report. |
| void PingManager::ReportSafeBrowsingHit( |
| const safe_browsing::HitReport& hit_report) { |
| auto resource_request = std::make_unique<network::ResourceRequest>(); |
| GURL report_url = SafeBrowsingHitUrl(hit_report); |
| resource_request->url = report_url; |
| resource_request->load_flags = net::LOAD_DISABLE_CACHE; |
| if (!hit_report.post_data.empty()) |
| resource_request->method = "POST"; |
| |
| auto report_ptr = network::SimpleURLLoader::Create( |
| std::move(resource_request), kTrafficAnnotation); |
| |
| if (!hit_report.post_data.empty()) |
| report_ptr->AttachStringForUpload(hit_report.post_data, "text/plain"); |
| |
| report_ptr->DownloadToStringOfUnboundedSizeUntilCrashAndDie( |
| url_loader_factory_.get(), |
| base::BindOnce(&PingManager::OnURLLoaderComplete, base::Unretained(this), |
| report_ptr.get())); |
| safebrowsing_reports_.insert(std::move(report_ptr)); |
| } |
| |
| // Sends threat details for users who opt-in. |
| void PingManager::ReportThreatDetails(const std::string& report) { |
| GURL report_url = ThreatDetailsUrl(); |
| |
| auto resource_request = std::make_unique<network::ResourceRequest>(); |
| resource_request->url = report_url; |
| resource_request->load_flags = net::LOAD_DISABLE_CACHE; |
| resource_request->method = "POST"; |
| |
| auto loader = network::SimpleURLLoader::Create(std::move(resource_request), |
| kTrafficAnnotation); |
| |
| loader->AttachStringForUpload(report, "application/octet-stream"); |
| |
| loader->DownloadToStringOfUnboundedSizeUntilCrashAndDie( |
| url_loader_factory_.get(), |
| base::BindOnce(&PingManager::OnURLLoaderComplete, base::Unretained(this), |
| loader.get())); |
| safebrowsing_reports_.insert(std::move(loader)); |
| } |
| |
| GURL PingManager::SafeBrowsingHitUrl( |
| const safe_browsing::HitReport& hit_report) const { |
| DCHECK(hit_report.threat_type == SB_THREAT_TYPE_URL_MALWARE || |
| hit_report.threat_type == SB_THREAT_TYPE_URL_PHISHING || |
| hit_report.threat_type == SB_THREAT_TYPE_URL_UNWANTED || |
| hit_report.threat_type == SB_THREAT_TYPE_URL_BINARY_MALWARE || |
| hit_report.threat_type == SB_THREAT_TYPE_URL_CLIENT_SIDE_PHISHING || |
| hit_report.threat_type == SB_THREAT_TYPE_URL_CLIENT_SIDE_MALWARE); |
| std::string url = |
| GetReportUrl(config_, "report", &hit_report.extended_reporting_level); |
| std::string threat_list = "none"; |
| switch (hit_report.threat_type) { |
| case SB_THREAT_TYPE_URL_MALWARE: |
| threat_list = "malblhit"; |
| break; |
| case SB_THREAT_TYPE_URL_PHISHING: |
| threat_list = "phishblhit"; |
| break; |
| case SB_THREAT_TYPE_URL_UNWANTED: |
| threat_list = "uwsblhit"; |
| break; |
| case SB_THREAT_TYPE_URL_BINARY_MALWARE: |
| threat_list = "binurlhit"; |
| break; |
| case SB_THREAT_TYPE_URL_CLIENT_SIDE_PHISHING: |
| threat_list = "phishcsdhit"; |
| break; |
| case SB_THREAT_TYPE_URL_CLIENT_SIDE_MALWARE: |
| threat_list = "malcsdhit"; |
| break; |
| default: |
| NOTREACHED(); |
| } |
| |
| std::string threat_source = "none"; |
| switch (hit_report.threat_source) { |
| case safe_browsing::ThreatSource::DATA_SAVER: |
| threat_source = "ds"; |
| break; |
| case safe_browsing::ThreatSource::REMOTE: |
| threat_source = "rem"; |
| break; |
| case safe_browsing::ThreatSource::LOCAL_PVER3: |
| threat_source = "l3"; |
| break; |
| case safe_browsing::ThreatSource::LOCAL_PVER4: |
| threat_source = "l4"; |
| break; |
| case safe_browsing::ThreatSource::CLIENT_SIDE_DETECTION: |
| threat_source = "csd"; |
| break; |
| case safe_browsing::ThreatSource::PASSWORD_PROTECTION_SERVICE: |
| threat_source = "pps"; |
| break; |
| case safe_browsing::ThreatSource::UNKNOWN: |
| NOTREACHED(); |
| } |
| |
| // Add user_population component only if it's not empty. |
| std::string user_population_comp; |
| if (!hit_report.population_id.empty()) { |
| // Population_id should be URL-safe, but escape it and size-limit it |
| // anyway since it came from outside Chrome. |
| std::string up_str = |
| net::EscapeQueryParamValue(hit_report.population_id, true); |
| if (up_str.size() > 512) { |
| DCHECK(false) << "population_id is too long: " << up_str; |
| up_str = "UP_STRING_TOO_LONG"; |
| } |
| |
| user_population_comp = "&up=" + up_str; |
| } |
| |
| return GURL(base::StringPrintf( |
| "%s&evts=%s&evtd=%s&evtr=%s&evhr=%s&evtb=%d&src=%s&m=%d%s", url.c_str(), |
| threat_list.c_str(), |
| net::EscapeQueryParamValue(hit_report.malicious_url.spec(), true).c_str(), |
| net::EscapeQueryParamValue(hit_report.page_url.spec(), true).c_str(), |
| net::EscapeQueryParamValue(hit_report.referrer_url.spec(), true).c_str(), |
| hit_report.is_subresource, threat_source.c_str(), |
| hit_report.is_metrics_reporting_active, user_population_comp.c_str())); |
| } |
| |
| GURL PingManager::ThreatDetailsUrl() const { |
| std::string url = GetReportUrl(config_, "clientreport/malware"); |
| return GURL(url); |
| } |
| |
| } // namespace safe_browsing |