Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(327)

Side by Side Diff: components/safe_browsing/password_protection/password_protection_service.cc

Issue 2747313002: PasswordProtectionService verdict cache management (Closed)
Patch Set: fix build Created 3 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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/metrics/histogram_macros.h" 10 #include "base/metrics/histogram_macros.h"
11 #include "base/strings/string_number_conversions.h"
12 #include "base/strings/string_util.h"
10 #include "components/safe_browsing_db/database_manager.h" 13 #include "components/safe_browsing_db/database_manager.h"
14 #include "components/safe_browsing_db/v4_protocol_manager_util.h"
11 #include "content/public/browser/browser_thread.h" 15 #include "content/public/browser/browser_thread.h"
12 16
13 using content::BrowserThread; 17 using content::BrowserThread;
14 18
15 namespace safe_browsing { 19 namespace safe_browsing {
16 20
21 namespace {
22
23 // Keys for storing password protection verdict into a DictionaryValue.
24 static const char kCacheTTL[] = "cache_ttl";
25 static const char kCacheCreationTime[] = "cache_creation_time";
26 static const char kVerdictType[] = "verdict";
27
28 // Helper function to determine if the given origin matches content settings
29 // map's patterns.
30 bool OriginMatchPrimaryPattern(
31 const GURL& origin,
32 const ContentSettingsPattern& primary_pattern,
33 const ContentSettingsPattern& secondary_pattern_unused) {
34 return ContentSettingsPattern::FromURLNoWildcard(origin) == primary_pattern;
35 }
36
37 // Convert a LoginReputationClientResponse proto into a DictionaryValue.
38 std::unique_ptr<base::DictionaryValue> CreateDictionaryFromVerdict(
39 LoginReputationClientResponse* verdict,
40 const base::Time& receive_time) {
41 std::unique_ptr<base::DictionaryValue> result =
42 base::MakeUnique<base::DictionaryValue>();
43 if (verdict) {
44 result->SetIntegerWithoutPathExpansion(kCacheTTL,
45 verdict->cache_duration_sec());
46 result->SetIntegerWithoutPathExpansion(
47 kCacheCreationTime, static_cast<int>(receive_time.ToDoubleT()));
48 result->SetIntegerWithoutPathExpansion(kVerdictType,
49 verdict->verdict_type());
50 }
51 return result;
52 }
53
54 } // namespace
55
17 PasswordProtectionService::PasswordProtectionService( 56 PasswordProtectionService::PasswordProtectionService(
18 const scoped_refptr<SafeBrowsingDatabaseManager>& database_manager) 57 const scoped_refptr<SafeBrowsingDatabaseManager>& database_manager)
19 : database_manager_(database_manager), weak_factory_(this) { 58 : database_manager_(database_manager), weak_factory_(this) {
20 DCHECK_CURRENTLY_ON(BrowserThread::UI); 59 DCHECK_CURRENTLY_ON(BrowserThread::UI);
21 } 60 }
22 61
23 PasswordProtectionService::~PasswordProtectionService() { 62 PasswordProtectionService::~PasswordProtectionService() {
24 weak_factory_.InvalidateWeakPtrs(); 63 weak_factory_.InvalidateWeakPtrs();
25 } 64 }
26 65
27 void PasswordProtectionService::RecordPasswordReuse(const GURL& url) { 66 void PasswordProtectionService::RecordPasswordReuse(const GURL& url) {
28 DCHECK_CURRENTLY_ON(BrowserThread::UI); 67 DCHECK_CURRENTLY_ON(BrowserThread::UI);
29 DCHECK(database_manager_); 68 DCHECK(database_manager_);
30 if (!url.is_valid()) 69 if (!url.is_valid())
31 return; 70 return;
32 71
33 BrowserThread::PostTaskAndReplyWithResult( 72 BrowserThread::PostTaskAndReplyWithResult(
34 BrowserThread::IO, FROM_HERE, 73 BrowserThread::IO, FROM_HERE,
35 base::Bind(&SafeBrowsingDatabaseManager::MatchCsdWhitelistUrl, 74 base::Bind(&SafeBrowsingDatabaseManager::MatchCsdWhitelistUrl,
36 database_manager_, url), 75 database_manager_, url),
37 base::Bind(&PasswordProtectionService::OnMatchCsdWhiteListResult, 76 base::Bind(&PasswordProtectionService::OnMatchCsdWhiteListResult,
38 GetWeakPtr())); 77 GetWeakPtr()));
39 } 78 }
40 79
80 LoginReputationClientResponse::VerdictType
81 PasswordProtectionService::GetCachedVerdict(HostContentSettingsMap* settings,
82 const GURL& url) {
83 // TODO(jialiul): Add UMA metrics to track if verdict is available, not
84 // available, or expired.
85 if (!settings || !url.is_valid()) {
86 return LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED;
87 }
88
89 GURL origin_url = url.GetOrigin();
90 std::unique_ptr<base::DictionaryValue> verdict_dictionary =
91 base::DictionaryValue::From(settings->GetWebsiteSetting(
92 origin_url, GURL(), CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION,
93 std::string(), nullptr));
94 // Return early if there is no verdict cached for this |origin_url|.
95 if (!verdict_dictionary.get() || verdict_dictionary->empty()) {
96 return LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED;
97 }
98
99 // For all the verdicts of the same origin, we key them by cache_expression
100 // field. And its corresponding value is a DictionaryValue contains its ttl,
101 // creation time, and verdict type info.
102 int cache_creation_time;
103 int cache_duration;
104 int verdict_type_value;
105 for (base::DictionaryValue::Iterator it(*verdict_dictionary.get());
106 !it.IsAtEnd(); it.Advance()) {
107 base::DictionaryValue* verdict_entry = nullptr;
108 DCHECK(verdict_dictionary->GetDictionaryWithoutPathExpansion(
109 it.key(), &verdict_entry));
lpz 2017/03/15 15:14:58 nit: alignment?
Jialiu Lin 2017/03/15 17:47:31 Acknowledged. git cl format insists this is the b
110 DCHECK(ParseVerdictEntry(verdict_entry, &cache_creation_time,
111 &cache_duration, &verdict_type_value));
112 if (UrlMatchCacheExpression(url, it.key() /* cache_expression */) &&
113 !IsCacheExpired(cache_creation_time, cache_duration)) {
114 return static_cast<LoginReputationClientResponse::VerdictType>(
115 verdict_type_value);
116 }
117 }
118 // We don't find any valid and matching verdict.
119 return LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED;
120 }
121
122 void PasswordProtectionService::CacheVerdict(
123 HostContentSettingsMap* settings,
124 const GURL& url,
125 LoginReputationClientResponse* verdict,
126 const base::Time& receive_time) {
127 if (!verdict)
128 return;
129
130 GURL origin_url = url.GetOrigin();
131 std::unique_ptr<base::DictionaryValue> verdict_dictionary =
132 base::DictionaryValue::From(settings->GetWebsiteSetting(
133 origin_url, GURL(), CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION,
134 std::string(), nullptr));
135
136 if (!verdict_dictionary.get())
137 verdict_dictionary = base::MakeUnique<base::DictionaryValue>();
138
139 std::unique_ptr<base::DictionaryValue> verdict_entry =
140 CreateDictionaryFromVerdict(verdict, receive_time);
141 // If same cache_expression is already in this verdict_dictionary, we simply
142 // override it with a fresher verdict.
143 // TODO(jialiul): We assume cache expressions returned by CSD server are
144 // not overlapping with each other and are stable. Need to confirm with SB
145 // backend if this assumption is always true.
146 verdict_dictionary->SetWithoutPathExpansion(verdict->cache_expression(),
147 std::move(verdict_entry));
148 settings->SetWebsiteSettingDefaultScope(
149 origin_url, GURL(), CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION,
150 std::string(), std::move(verdict_dictionary));
151 }
152
41 void PasswordProtectionService::OnMatchCsdWhiteListResult( 153 void PasswordProtectionService::OnMatchCsdWhiteListResult(
42 bool match_whitelist) { 154 bool match_whitelist) {
43 DCHECK_CURRENTLY_ON(BrowserThread::UI); 155 DCHECK_CURRENTLY_ON(BrowserThread::UI);
44 UMA_HISTOGRAM_BOOLEAN( 156 UMA_HISTOGRAM_BOOLEAN(
45 "PasswordManager.PasswordReuse.MainFrameMatchCsdWhitelist", 157 "PasswordManager.PasswordReuse.MainFrameMatchCsdWhitelist",
46 match_whitelist); 158 match_whitelist);
47 } 159 }
48 160
161 HostContentSettingsMap*
162 PasswordProtectionService::GetSettingMapForActiveProfile() {
163 return nullptr;
164 }
165
166 void PasswordProtectionService::OnURLsDeleted(
167 history::HistoryService* history_service,
168 bool all_history,
169 bool expired,
170 const history::URLRows& deleted_rows,
171 const std::set<GURL>& favicon_urls) {
172 HostContentSettingsMap* setting_map = GetSettingMapForActiveProfile();
173 if (!setting_map)
174 return;
175
176 BrowserThread::PostTask(
177 BrowserThread::UI, FROM_HERE,
178 base::Bind(&PasswordProtectionService::RemoveContentSettingsOnURLsDeleted,
179 GetWeakPtr(), setting_map, all_history, deleted_rows));
180 }
181
182 void PasswordProtectionService::RemoveContentSettingsOnURLsDeleted(
183 HostContentSettingsMap* setting_map,
184 bool all_history,
185 const history::URLRows& deleted_rows) {
186 DCHECK_CURRENTLY_ON(BrowserThread::UI);
187 if (all_history) {
188 setting_map->ClearSettingsForOneType(
189 CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION);
190 return;
191 }
192
193 // For now, if a URL is deleted from history, we simply remove all the
194 // cached verdicts of the same origin. This is a pretty aggressive deletion.
195 // We might revisit this logic later to decide if we want to only delete the
196 // cached verdict whose cache expression matches this URL.
197 for (const history::URLRow& row : deleted_rows) {
198 setting_map->ClearSettingsForOneTypeWithPredicate(
199 CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION,
200 base::Bind(&OriginMatchPrimaryPattern, row.url().GetOrigin()));
201 }
202 }
203
204 // static
205 bool PasswordProtectionService::ParseVerdictEntry(
206 base::DictionaryValue* verdict_entry,
207 int* out_cache_creation_time,
208 int* out_cache_duration,
209 int* out_verdict_type) {
210 return verdict_entry &&
211 verdict_entry->GetInteger(kCacheCreationTime,
212 out_cache_creation_time) &&
213 verdict_entry->GetInteger(kCacheTTL, out_cache_duration) &&
214 verdict_entry->GetInteger(kVerdictType, out_verdict_type);
215 }
216
217 bool PasswordProtectionService::UrlMatchCacheExpression(
218 const GURL& url,
219 const std::string& cache_expression) {
220 std::string canon_host;
221 std::string canon_path;
222 std::string canon_query;
223 V4ProtocolManagerUtil::CanonicalizeUrl(url, &canon_host, &canon_path,
224 &canon_query);
225
226 std::vector<std::string> hosts;
227 if (url.HostIsIPAddress()) {
228 hosts.push_back(url.host());
229 } else {
230 V4ProtocolManagerUtil::GenerateHostVariantsToCheck(canon_host, &hosts);
231 }
232
233 std::vector<std::string> paths;
234 V4ProtocolManagerUtil::GeneratePathVariantsToCheck(canon_path, canon_query,
235 &paths);
236
237 base::StringPiece cache_expression_trimed(base::TrimString(
238 cache_expression, "/", base::TrimPositions::TRIM_TRAILING));
239 for (const std::string& host : hosts) {
240 for (const std::string& path : paths) {
241 std::string host_path_combine(host);
242 host_path_combine.append(path);
243 if (cache_expression_trimed ==
244 base::TrimString(host_path_combine, "/",
245 base::TrimPositions::TRIM_TRAILING)) {
246 return true;
247 }
248 }
249 }
250 return false;
251 }
252
253 bool PasswordProtectionService::IsCacheExpired(int cache_creation_time,
254 int cache_duration) {
255 // TODO(jialiul): For now, we assume client's clock is accurate or almost
256 // accurate. Need some logic to handle cases where client's clock is way off.
257 return base::Time::Now().ToDoubleT() >
258 static_cast<double>(cache_creation_time + cache_duration);
259 }
260
49 } // namespace safe_browsing 261 } // namespace safe_browsing
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698