blob: 47707316874d2c23948c804321abea17a5300bf5 [file] [log] [blame]
Avi Drissman4e1b7bc32022-09-15 14:03:501// Copyright 2020 The Chromium Authors
Alexander Timin1cc31f42020-05-12 16:26:012// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
danakjc492bf82020-09-09 20:02:445#include "content/browser/renderer_host/cookie_utils.h"
Alexander Timin1cc31f42020-05-12 16:26:016
Jayson Adams295430202021-07-27 01:07:577#include "base/ranges/algorithm.h"
Alexander Timin1cc31f42020-05-12 16:26:018#include "content/browser/devtools/devtools_instrumentation.h"
danakjc492bf82020-09-09 20:02:449#include "content/browser/renderer_host/render_frame_host_impl.h"
Alexander Timin1cc31f42020-05-12 16:26:0110#include "content/public/browser/browser_context.h"
11#include "content/public/browser/cookie_access_details.h"
12#include "content/public/common/content_client.h"
Jihwan Marc Kim3e132f12020-05-20 17:33:1913#include "net/cookies/cookie_inclusion_status.h"
sbingler870b63f2023-04-25 16:47:5814#include "services/metrics/public/cpp/metrics_utils.h"
Alexander Timin1cc31f42020-05-12 16:26:0115#include "services/metrics/public/cpp/ukm_builders.h"
16
17namespace content {
18
19namespace {
20
selya27d093e2023-08-02 22:20:0021void RecordPartitionedCookieUseUKM(RenderFrameHost* rfh,
22 bool partitioned_cookies_exist) {
23 // Our data collection policy disallows collecting UKMs while prerendering.
24 // See //content/browser/preloading/prerender/README.md and ask the team to
25 // explore options to record data for prerendering pages if we need to
26 // support the case.
27 if (rfh->IsInLifecycleState(RenderFrameHost::LifecycleState::kPrerendering)) {
28 return;
29 }
selya9d48d7f2023-08-10 14:19:0430 if (!partitioned_cookies_exist) {
31 return;
32 }
selya27d093e2023-08-02 22:20:0033 ukm::SourceId source_id = rfh->GetPageUkmSourceId();
34
35 ukm::builders::PartitionedCookiePresent(source_id)
36 .SetPartitionedCookiePresent(partitioned_cookies_exist)
37 .Record(ukm::UkmRecorder::Get());
38}
39
sbingler870b63f2023-04-25 16:47:5840void RecordRedirectContextDowngradeUKM(RenderFrameHost* rfh,
41 CookieAccessDetails::Type access_type,
42 const net::CanonicalCookie& cookie,
43 const GURL& url) {
44 CHECK(rfh);
Takashi Toyoshimad540b932023-06-30 02:30:2745
46 // Our data collection policy disallows collecting UKMs while prerendering.
47 // See //content/browser/preloading/prerender/README.md and ask the team to
48 // explore options to record data for prerendering pages if we need to
49 // support the case.
50 if (rfh->IsInLifecycleState(RenderFrameHost::LifecycleState::kPrerendering)) {
51 return;
52 }
53
sbingler870b63f2023-04-25 16:47:5854 ukm::SourceId source_id = rfh->GetPageUkmSourceId();
55
56 int64_t samesite_value = static_cast<int64_t>(cookie.SameSite());
57 if (access_type == CookieAccessDetails::Type::kRead) {
58 base::TimeDelta cookie_age = base::Time::Now() - cookie.CreationDate();
59
60 ukm::builders::SamesiteRedirectContextDowngrade(source_id)
61 .SetSamesiteValueReadPerCookie(samesite_value)
62 .SetAgePerCookie(
63 ukm::GetExponentialBucketMinForUserTiming(cookie_age.InMinutes()))
64 .Record(ukm::UkmRecorder::Get());
65 } else {
66 CHECK(access_type == CookieAccessDetails::Type::kChange);
67 ukm::builders::SamesiteRedirectContextDowngrade(source_id)
68 .SetSamesiteValueWritePerCookie(samesite_value)
69 .Record(ukm::UkmRecorder::Get());
70 }
71}
72
73void RecordSchemefulContextDowngradeUKM(
74 RenderFrameHost* rfh,
75 CookieAccessDetails::Type access_type,
76 const net::CookieInclusionStatus& status,
77 const GURL& url) {
78 CHECK(rfh);
Takashi Toyoshimad540b932023-06-30 02:30:2779
80 // Our data collection policy disallows collecting UKMs while prerendering.
81 // See //content/browser/preloading/prerender/README.md and ask the team to
82 // explore options to record data for prerendering pages if we need to
83 // support the case.
84 if (rfh->IsInLifecycleState(RenderFrameHost::LifecycleState::kPrerendering)) {
85 return;
86 }
87
Alexander Timin1cc31f42020-05-12 16:26:0188 ukm::SourceId source_id = rfh->GetPageUkmSourceId();
89
Lei Zhang3ab30192022-08-15 19:52:2090 auto downgrade_metric =
91 static_cast<int64_t>(status.GetBreakingDowngradeMetricsEnumValue(url));
Alexander Timin1cc31f42020-05-12 16:26:0192 if (access_type == CookieAccessDetails::Type::kRead) {
93 ukm::builders::SchemefulSameSiteContextDowngrade(source_id)
Lei Zhang3ab30192022-08-15 19:52:2094 .SetRequestPerCookie(downgrade_metric)
Alexander Timin1cc31f42020-05-12 16:26:0195 .Record(ukm::UkmRecorder::Get());
96 } else {
sbingler870b63f2023-04-25 16:47:5897 CHECK(access_type == CookieAccessDetails::Type::kChange);
Alexander Timin1cc31f42020-05-12 16:26:0198 ukm::builders::SchemefulSameSiteContextDowngrade(source_id)
Lei Zhang3ab30192022-08-15 19:52:2099 .SetResponsePerCookie(downgrade_metric)
Alexander Timin1cc31f42020-05-12 16:26:01100 .Record(ukm::UkmRecorder::Get());
101 }
102}
103
Simon Zünd657178e2021-05-27 06:19:55104bool ShouldReportDevToolsIssueForStatus(
105 const net::CookieInclusionStatus& status) {
106 return status.ShouldWarn() ||
107 status.HasExclusionReason(
Dylan Cutler15fdd1e2022-11-15 22:54:46108 net::CookieInclusionStatus::EXCLUDE_DOMAIN_NON_ASCII) ||
109 status.HasExclusionReason(
110 net::CookieInclusionStatus::
111 EXCLUDE_THIRD_PARTY_BLOCKED_WITHIN_FIRST_PARTY_SET);
Simon Zünd657178e2021-05-27 06:19:55112}
113
Alexander Timin1cc31f42020-05-12 16:26:01114} // namespace
115
116void SplitCookiesIntoAllowedAndBlocked(
117 const network::mojom::CookieAccessDetailsPtr& cookie_details,
118 CookieAccessDetails* allowed,
119 CookieAccessDetails* blocked) {
120 *allowed =
121 CookieAccessDetails({cookie_details->type,
122 cookie_details->url,
123 cookie_details->site_for_cookies.RepresentativeUrl(),
124 {},
125 /* blocked_by_policy=*/false});
Jayson Adams295430202021-07-27 01:07:57126 int allowed_count = base::ranges::count_if(
127 cookie_details->cookie_list,
128 [](const network::mojom::CookieOrLineWithAccessResultPtr&
129 cookie_and_access_result) {
130 // "Included" cookies have no exclusion reasons so we don't also have to
131 // check for !(net::CookieInclusionStatus::EXCLUDE_USER_PREFERENCES).
132 return cookie_and_access_result->access_result.status.IsInclude();
133 });
134 allowed->cookie_list.reserve(allowed_count);
135
Alexander Timin1cc31f42020-05-12 16:26:01136 *blocked =
137 CookieAccessDetails({cookie_details->type,
138 cookie_details->url,
139 cookie_details->site_for_cookies.RepresentativeUrl(),
140 {},
141 /* blocked_by_policy=*/true});
Jayson Adams295430202021-07-27 01:07:57142 int blocked_count = base::ranges::count_if(
143 cookie_details->cookie_list,
144 [](const network::mojom::CookieOrLineWithAccessResultPtr&
145 cookie_and_access_result) {
146 return cookie_and_access_result->access_result.status
Dylan Cutler0ed68252022-10-17 16:54:33147 .ExcludedByUserPreferences();
Jayson Adams295430202021-07-27 01:07:57148 });
149 blocked->cookie_list.reserve(blocked_count);
Alexander Timin1cc31f42020-05-12 16:26:01150
Jayson Adams295430202021-07-27 01:07:57151 for (const auto& cookie_and_access_result : cookie_details->cookie_list) {
Dylan Cutler0ed68252022-10-17 16:54:33152 if (cookie_and_access_result->access_result.status
153 .ExcludedByUserPreferences()) {
Jayson Adams295430202021-07-27 01:07:57154 blocked->cookie_list.emplace_back(
cfredric76b2d222021-01-27 20:12:04155 std::move(cookie_and_access_result->cookie_or_line->get_cookie()));
156 } else if (cookie_and_access_result->access_result.status.IsInclude()) {
Jayson Adams295430202021-07-27 01:07:57157 allowed->cookie_list.emplace_back(
cfredric76b2d222021-01-27 20:12:04158 std::move(cookie_and_access_result->cookie_or_line->get_cookie()));
Alexander Timin1cc31f42020-05-12 16:26:01159 }
160 }
161}
162
cfredrica5fb0982021-01-09 00:18:01163void EmitCookieWarningsAndMetrics(
Alexander Timin1cc31f42020-05-12 16:26:01164 RenderFrameHostImpl* rfh,
165 const network::mojom::CookieAccessDetailsPtr& cookie_details) {
166 RenderFrameHostImpl* root_frame_host = rfh->GetMainFrame();
167
Sreeja Kamishettye49854f82021-06-02 00:52:03168 if (!root_frame_host->IsActive())
Alexander Timin1cc31f42020-05-12 16:26:01169 return;
170
171 bool samesite_treated_as_lax_cookies = false;
172 bool samesite_none_insecure_cookies = false;
Alexander Timin1cc31f42020-05-12 16:26:01173 bool breaking_context_downgrade = false;
Lily Chenc4423c02021-03-11 16:02:02174 bool lax_allow_unsafe_cookies = false;
Alexander Timin1cc31f42020-05-12 16:26:01175
Lily Chen2db3a422021-07-20 18:02:25176 bool samesite_cookie_inclusion_changed_by_cross_site_redirect = false;
177
Dylan Cutler8d5f8912022-03-04 17:39:19178 bool partitioned_cookies_exist = false;
179
Ari Chivukula5f21c112022-04-26 19:23:34180 bool cookie_has_not_been_refreshed_in_201_to_300_days = false;
181 bool cookie_has_not_been_refreshed_in_301_to_350_days = false;
182 bool cookie_has_not_been_refreshed_in_351_to_400_days = false;
183
Johann Hofmanne5764d12022-07-13 23:06:28184 bool cookie_has_domain_non_ascii = false;
185
cfredric76b2d222021-01-27 20:12:04186 for (const network::mojom::CookieOrLineWithAccessResultPtr& cookie :
Alexander Timin1cc31f42020-05-12 16:26:01187 cookie_details->cookie_list) {
Johann Hofmann975c0b42022-08-02 21:05:51188 const net::CookieInclusionStatus& status = cookie->access_result.status;
189 if (ShouldReportDevToolsIssueForStatus(status)) {
Juba Borgohainc93969e2022-02-25 21:56:12190 devtools_instrumentation::ReportCookieIssue(
Simon Zünd657178e2021-05-27 06:19:55191 root_frame_host, cookie, cookie_details->url,
192 cookie_details->site_for_cookies,
193 cookie_details->type == CookieAccessDetails::Type::kRead
Juba Borgohainc93969e2022-02-25 21:56:12194 ? blink::mojom::CookieOperation::kReadCookie
195 : blink::mojom::CookieOperation::kSetCookie,
Simon Zünd657178e2021-05-27 06:19:55196 cookie_details->devtools_request_id);
197 }
198
cfredric76b2d222021-01-27 20:12:04199 if (cookie->access_result.status.ShouldWarn()) {
Lily Chen9de4065b2020-06-24 20:18:47200 samesite_treated_as_lax_cookies =
201 samesite_treated_as_lax_cookies ||
cfredrica5fb0982021-01-09 00:18:01202 status.HasWarningReason(
Jihwan Marc Kim3e132f12020-05-20 17:33:19203 net::CookieInclusionStatus::
Lily Chen9de4065b2020-06-24 20:18:47204 WARN_SAMESITE_UNSPECIFIED_CROSS_SITE_CONTEXT) ||
cfredrica5fb0982021-01-09 00:18:01205 status.HasWarningReason(
Jihwan Marc Kim3e132f12020-05-20 17:33:19206 net::CookieInclusionStatus::
Lily Chen9de4065b2020-06-24 20:18:47207 WARN_SAMESITE_UNSPECIFIED_LAX_ALLOW_UNSAFE);
Alexander Timin1cc31f42020-05-12 16:26:01208
Lily Chen9de4065b2020-06-24 20:18:47209 samesite_none_insecure_cookies =
210 samesite_none_insecure_cookies ||
cfredrica5fb0982021-01-09 00:18:01211 status.HasWarningReason(
Lily Chen9de4065b2020-06-24 20:18:47212 net::CookieInclusionStatus::WARN_SAMESITE_NONE_INSECURE);
213
Lily Chenc4423c02021-03-11 16:02:02214 lax_allow_unsafe_cookies =
215 lax_allow_unsafe_cookies ||
216 status.HasWarningReason(
217 net::CookieInclusionStatus::
218 WARN_SAMESITE_UNSPECIFIED_LAX_ALLOW_UNSAFE);
219
Lily Chen2db3a422021-07-20 18:02:25220 samesite_cookie_inclusion_changed_by_cross_site_redirect =
221 samesite_cookie_inclusion_changed_by_cross_site_redirect ||
222 status.HasWarningReason(
223 net::CookieInclusionStatus::
224 WARN_CROSS_SITE_REDIRECT_DOWNGRADE_CHANGES_INCLUSION);
Alexander Timin1cc31f42020-05-12 16:26:01225 }
Alexander Timin1cc31f42020-05-12 16:26:01226
Johann Hofmann975c0b42022-08-02 21:05:51227 cookie_has_domain_non_ascii =
228 cookie_has_domain_non_ascii ||
229 status.HasWarningReason(
230 net::CookieInclusionStatus::WARN_DOMAIN_NON_ASCII) ||
231 status.HasExclusionReason(
232 net::CookieInclusionStatus::EXCLUDE_DOMAIN_NON_ASCII);
233
Dylan Cutler8d5f8912022-03-04 17:39:19234 partitioned_cookies_exist =
235 partitioned_cookies_exist ||
236 (cookie->cookie_or_line->is_cookie() &&
Dylan Cutler051411b42022-07-12 22:20:07237 cookie->cookie_or_line->get_cookie().IsPartitioned() &&
238 // Ignore nonced partition keys since this metric is meant to track
239 // usage of the Partitioned attribute.
240 !cookie->cookie_or_line->get_cookie().PartitionKey()->nonce());
Dylan Cutler8d5f8912022-03-04 17:39:19241
selya27d093e2023-08-02 22:20:00242 RecordPartitionedCookieUseUKM(rfh, partitioned_cookies_exist);
243
Ayu Ishii2e3998902020-07-14 18:22:30244 breaking_context_downgrade =
245 breaking_context_downgrade ||
sbingler870b63f2023-04-25 16:47:58246 cookie->access_result.status.HasSchemefulDowngradeWarning();
Alexander Timin1cc31f42020-05-12 16:26:01247
sbingler870b63f2023-04-25 16:47:58248 if (cookie->access_result.status.HasSchemefulDowngradeWarning()) {
249 // Unlike with UMA, do not record cookies that have no schemeful downgrade
250 // warning.
251 RecordSchemefulContextDowngradeUKM(rfh, cookie_details->type,
252 cookie->access_result.status,
253 cookie_details->url);
254 }
255
256 if (status.HasWarningReason(
257 net::CookieInclusionStatus::
258 WARN_CROSS_SITE_REDIRECT_DOWNGRADE_CHANGES_INCLUSION) &&
259 cookie->cookie_or_line->is_cookie()) {
260 RecordRedirectContextDowngradeUKM(rfh, cookie_details->type,
261 cookie->cookie_or_line->get_cookie(),
262 cookie_details->url);
Alexander Timin1cc31f42020-05-12 16:26:01263 }
Ari Chivukula5f21c112022-04-26 19:23:34264
265 // In order to anticipate the potential effects of the expiry limit in
266 // rfc6265bis, we need to check how long it's been since the cookie was
267 // refreshed (if LastUpdateDate is populated). These three buckets were
268 // picked so we could engage sites with some granularity around urgency.
269 // We ignore the space under 200 days as these cookies are not at risk
270 // of expiring and we ignore the space over 400 days as these cookies
271 // have already expired. Metrics will take 200 days from M103 to populate.
272 base::Time last_update_date =
273 cookie->cookie_or_line->is_cookie()
274 ? cookie->cookie_or_line->get_cookie().LastUpdateDate()
275 : base::Time();
276 if (!last_update_date.is_null()) {
277 int days_since_refresh = (base::Time::Now() - last_update_date).InDays();
278 cookie_has_not_been_refreshed_in_201_to_300_days |=
279 days_since_refresh > 200 && days_since_refresh <= 300;
280 cookie_has_not_been_refreshed_in_301_to_350_days |=
281 days_since_refresh > 300 && days_since_refresh <= 350;
282 cookie_has_not_been_refreshed_in_351_to_400_days |=
283 days_since_refresh > 350 && days_since_refresh <= 400;
284 }
Alexander Timin1cc31f42020-05-12 16:26:01285 }
286
Alexander Timin1cc31f42020-05-12 16:26:01287 if (samesite_treated_as_lax_cookies) {
288 GetContentClient()->browser()->LogWebFeatureForCurrentPage(
289 rfh, blink::mojom::WebFeature::kCookieNoSameSite);
290 }
291
292 if (samesite_none_insecure_cookies) {
293 GetContentClient()->browser()->LogWebFeatureForCurrentPage(
294 rfh, blink::mojom::WebFeature::kCookieInsecureAndSameSiteNone);
295 }
296
297 if (breaking_context_downgrade) {
298 GetContentClient()->browser()->LogWebFeatureForCurrentPage(
299 rfh, blink::mojom::WebFeature::kSchemefulSameSiteContextDowngrade);
300 }
cfredrica5fb0982021-01-09 00:18:01301
Lily Chenc4423c02021-03-11 16:02:02302 if (lax_allow_unsafe_cookies) {
303 GetContentClient()->browser()->LogWebFeatureForCurrentPage(
304 rfh, blink::mojom::WebFeature::kLaxAllowingUnsafeCookies);
305 }
306
Lily Chen2db3a422021-07-20 18:02:25307 if (samesite_cookie_inclusion_changed_by_cross_site_redirect) {
308 GetContentClient()->browser()->LogWebFeatureForCurrentPage(
309 rfh, blink::mojom::WebFeature::
310 kSameSiteCookieInclusionChangedByCrossSiteRedirect);
311 }
Dylan Cutler8d5f8912022-03-04 17:39:19312
313 if (partitioned_cookies_exist) {
314 GetContentClient()->browser()->LogWebFeatureForCurrentPage(
315 rfh, blink::mojom::WebFeature::kPartitionedCookies);
316 }
Ari Chivukula5f21c112022-04-26 19:23:34317
318 if (cookie_has_not_been_refreshed_in_201_to_300_days) {
319 GetContentClient()->browser()->LogWebFeatureForCurrentPage(
320 rfh,
321 blink::mojom::WebFeature::kCookieHasNotBeenRefreshedIn201To300Days);
322 }
323
324 if (cookie_has_not_been_refreshed_in_301_to_350_days) {
325 GetContentClient()->browser()->LogWebFeatureForCurrentPage(
326 rfh,
327 blink::mojom::WebFeature::kCookieHasNotBeenRefreshedIn301To350Days);
328 }
329
330 if (cookie_has_not_been_refreshed_in_351_to_400_days) {
331 GetContentClient()->browser()->LogWebFeatureForCurrentPage(
332 rfh,
333 blink::mojom::WebFeature::kCookieHasNotBeenRefreshedIn351To400Days);
334 }
Johann Hofmanne5764d12022-07-13 23:06:28335
336 if (cookie_has_domain_non_ascii) {
337 GetContentClient()->browser()->LogWebFeatureForCurrentPage(
338 rfh, blink::mojom::WebFeature::kCookieDomainNonASCII);
339 }
Alexander Timin1cc31f42020-05-12 16:26:01340}
341
342} // namespace content