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

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

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

Powered by Google App Engine
This is Rietveld 408576698