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

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

Issue 2747313002: PasswordProtectionService verdict cache management (Closed)
Patch Set: remove extra deps 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,
vakh (use Gerrit instead) 2017/03/16 22:04:30 this can be const
Jialiu Lin 2017/03/18 23:46:31 Done.
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,
vakh (use Gerrit instead) 2017/03/16 22:04:29 const *settings?
Jialiu Lin 2017/03/18 23:46:31 Done.
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()) {
Nathan Parker 2017/03/16 21:27:11 nit: do you want to DCHECK on settings, or is ther
Jialiu Lin 2017/03/18 23:46:31 Done.
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 base::DictionaryValue* verdict_entry = nullptr;
106 for (base::DictionaryValue::Iterator it(*verdict_dictionary.get());
107 !it.IsAtEnd(); it.Advance()) {
108 if (verdict_dictionary->GetDictionaryWithoutPathExpansion(it.key(),
109 &verdict_entry) &&
Nathan Parker 2017/03/16 21:27:11 Is it an error if ParseVerdictEntry() returns fals
Jialiu Lin 2017/03/18 23:46:31 Done.
110 ParseVerdictEntry(verdict_entry, &cache_creation_time, &cache_duration,
111 &verdict_type_value)) {
112 if (UrlMatchCacheExpression(url, it.key() /* cache_expression */) &&
113 !IsCacheExpired(cache_creation_time, cache_duration)) {
Nathan Parker 2017/03/16 21:27:11 Do you think we need to remove expired entries, or
Jialiu Lin 2017/03/18 23:46:31 If the entry is expired, we are going to request a
114 return static_cast<LoginReputationClientResponse::VerdictType>(
115 verdict_type_value);
Nathan Parker 2017/03/16 21:27:11 Something to think about: If we were to add new ve
Jialiu Lin 2017/03/18 23:46:31 Agreed. In the latest patch, I cache the serialize
116 }
117 }
118 }
119 // We don't find any valid and matching verdict.
120 return LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED;
121 }
122
123 void PasswordProtectionService::CacheVerdict(
124 HostContentSettingsMap* settings,
vakh (use Gerrit instead) 2017/03/16 22:04:30 Since |settings| is being set, it is an output, so
Jialiu Lin 2017/03/18 23:46:31 Done.
125 const GURL& url,
126 LoginReputationClientResponse* verdict,
127 const base::Time& receive_time) {
128 if (!verdict)
Nathan Parker 2017/03/16 21:27:11 Again, how about DCHECK instead?
Jialiu Lin 2017/03/18 23:46:31 Done.
129 return;
130
131 GURL origin_url = url.GetOrigin();
132 std::unique_ptr<base::DictionaryValue> verdict_dictionary =
133 base::DictionaryValue::From(settings->GetWebsiteSetting(
134 origin_url, GURL(), CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION,
135 std::string(), nullptr));
136
137 if (!verdict_dictionary.get())
138 verdict_dictionary = base::MakeUnique<base::DictionaryValue>();
139
140 std::unique_ptr<base::DictionaryValue> verdict_entry =
141 CreateDictionaryFromVerdict(verdict, receive_time);
142 // If same cache_expression is already in this verdict_dictionary, we simply
143 // override it with a fresher verdict.
144 // TODO(jialiul): We assume cache expressions returned by CSD server are
145 // not overlapping with each other and are stable. Need to confirm with SB
Nathan Parker 2017/03/16 21:27:11 We could verify it on the client and do something
Jialiu Lin 2017/03/18 23:46:31 You're right. I just confirmed with Weining. Overl
146 // backend if this assumption is always true.
147 verdict_dictionary->SetWithoutPathExpansion(verdict->cache_expression(),
148 std::move(verdict_entry));
149 settings->SetWebsiteSettingDefaultScope(
150 origin_url, GURL(), CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION,
151 std::string(), std::move(verdict_dictionary));
152 }
153
41 void PasswordProtectionService::OnMatchCsdWhiteListResult( 154 void PasswordProtectionService::OnMatchCsdWhiteListResult(
42 bool match_whitelist) { 155 bool match_whitelist) {
43 DCHECK_CURRENTLY_ON(BrowserThread::UI); 156 DCHECK_CURRENTLY_ON(BrowserThread::UI);
44 UMA_HISTOGRAM_BOOLEAN( 157 UMA_HISTOGRAM_BOOLEAN(
45 "PasswordManager.PasswordReuse.MainFrameMatchCsdWhitelist", 158 "PasswordManager.PasswordReuse.MainFrameMatchCsdWhitelist",
46 match_whitelist); 159 match_whitelist);
47 } 160 }
48 161
162 HostContentSettingsMap*
163 PasswordProtectionService::GetSettingMapForActiveProfile() {
164 return nullptr;
165 }
166
167 void PasswordProtectionService::OnURLsDeleted(
168 history::HistoryService* history_service,
169 bool all_history,
170 bool expired,
171 const history::URLRows& deleted_rows,
172 const std::set<GURL>& favicon_urls) {
173 HostContentSettingsMap* setting_map = GetSettingMapForActiveProfile();
174 if (!setting_map)
175 return;
176
177 BrowserThread::PostTask(
178 BrowserThread::UI, FROM_HERE,
179 base::Bind(&PasswordProtectionService::RemoveContentSettingsOnURLsDeleted,
180 GetWeakPtr(), setting_map, all_history, deleted_rows));
181 }
182
183 void PasswordProtectionService::RemoveContentSettingsOnURLsDeleted(
184 HostContentSettingsMap* setting_map,
vakh (use Gerrit instead) 2017/03/16 22:04:30 |setting_map| at the end
Jialiu Lin 2017/03/18 23:46:31 Done.
185 bool all_history,
186 const history::URLRows& deleted_rows) {
187 DCHECK_CURRENTLY_ON(BrowserThread::UI);
188 if (all_history) {
189 setting_map->ClearSettingsForOneType(
190 CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION);
191 return;
192 }
193
194 // For now, if a URL is deleted from history, we simply remove all the
195 // cached verdicts of the same origin. This is a pretty aggressive deletion.
196 // We might revisit this logic later to decide if we want to only delete the
197 // cached verdict whose cache expression matches this URL.
198 for (const history::URLRow& row : deleted_rows) {
199 setting_map->ClearSettingsForOneTypeWithPredicate(
200 CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION,
201 base::Bind(&OriginMatchPrimaryPattern, row.url().GetOrigin()));
202 }
203 }
204
205 // static
206 bool PasswordProtectionService::ParseVerdictEntry(
207 base::DictionaryValue* verdict_entry,
vakh (use Gerrit instead) 2017/03/16 22:04:30 |verdict_entry| const?
Jialiu Lin 2017/03/18 23:46:31 acknowledged. for some weird reason, base::Dictio
208 int* out_cache_creation_time,
209 int* out_cache_duration,
210 int* out_verdict_type) {
211 return verdict_entry &&
212 verdict_entry->GetInteger(kCacheCreationTime,
213 out_cache_creation_time) &&
214 verdict_entry->GetInteger(kCacheTTL, out_cache_duration) &&
215 verdict_entry->GetInteger(kVerdictType, out_verdict_type);
216 }
217
218 bool PasswordProtectionService::UrlMatchCacheExpression(
219 const GURL& url,
220 const std::string& cache_expression) {
221 std::string canon_host;
222 std::string canon_path;
223 std::string canon_query;
224 V4ProtocolManagerUtil::CanonicalizeUrl(url, &canon_host, &canon_path,
225 &canon_query);
226
227 std::vector<std::string> hosts;
228 if (url.HostIsIPAddress()) {
229 hosts.push_back(url.host());
230 } else {
231 V4ProtocolManagerUtil::GenerateHostVariantsToCheck(canon_host, &hosts);
232 }
233
234 std::vector<std::string> paths;
235 V4ProtocolManagerUtil::GeneratePathVariantsToCheck(canon_path, canon_query,
236 &paths);
237
238 base::StringPiece cache_expression_trimed(base::TrimString(
239 cache_expression, "/", base::TrimPositions::TRIM_TRAILING));
240 for (const std::string& host : hosts) {
Nathan Parker 2017/03/16 21:27:11 I'm a little worried about CPU/latency here, since
Jialiu Lin 2017/03/18 23:46:31 Rewrote this function significantly. Now only one
241 for (const std::string& path : paths) {
vakh (use Gerrit instead) 2017/03/16 22:04:30 Most code in this function is similar to V4Protoco
Jialiu Lin 2017/03/18 23:46:31 This function is rewritten significantly in the la
242 std::string host_path_combine(host);
243 host_path_combine.append(path);
244 if (cache_expression_trimed ==
245 base::TrimString(host_path_combine, "/",
246 base::TrimPositions::TRIM_TRAILING)) {
247 return true;
248 }
249 }
250 }
251 return false;
252 }
253
254 bool PasswordProtectionService::IsCacheExpired(int cache_creation_time,
255 int cache_duration) {
256 // TODO(jialiul): For now, we assume client's clock is accurate or almost
257 // accurate. Need some logic to handle cases where client's clock is way off.
258 return base::Time::Now().ToDoubleT() >
259 static_cast<double>(cache_creation_time + cache_duration);
260 }
261
49 } // namespace safe_browsing 262 } // namespace safe_browsing
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698