blob: f4bd86fd00f6348566995e22060197a4fb445054 [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::
Shuran Huangdc0b8d82023-09-19 14:49:30111 EXCLUDE_THIRD_PARTY_BLOCKED_WITHIN_FIRST_PARTY_SET) ||
112 status.HasExclusionReason(
113 net::CookieInclusionStatus::EXCLUDE_THIRD_PARTY_PHASEOUT);
Simon Zünd657178e2021-05-27 06:19:55114}
115
Alexander Timin1cc31f42020-05-12 16:26:01116} // namespace
117
118void SplitCookiesIntoAllowedAndBlocked(
119 const network::mojom::CookieAccessDetailsPtr& cookie_details,
120 CookieAccessDetails* allowed,
121 CookieAccessDetails* blocked) {
122 *allowed =
123 CookieAccessDetails({cookie_details->type,
124 cookie_details->url,
125 cookie_details->site_for_cookies.RepresentativeUrl(),
126 {},
127 /* blocked_by_policy=*/false});
Jayson Adams295430202021-07-27 01:07:57128 int allowed_count = base::ranges::count_if(
129 cookie_details->cookie_list,
130 [](const network::mojom::CookieOrLineWithAccessResultPtr&
131 cookie_and_access_result) {
132 // "Included" cookies have no exclusion reasons so we don't also have to
133 // check for !(net::CookieInclusionStatus::EXCLUDE_USER_PREFERENCES).
134 return cookie_and_access_result->access_result.status.IsInclude();
135 });
136 allowed->cookie_list.reserve(allowed_count);
137
Alexander Timin1cc31f42020-05-12 16:26:01138 *blocked =
139 CookieAccessDetails({cookie_details->type,
140 cookie_details->url,
141 cookie_details->site_for_cookies.RepresentativeUrl(),
142 {},
143 /* blocked_by_policy=*/true});
Jayson Adams295430202021-07-27 01:07:57144 int blocked_count = base::ranges::count_if(
145 cookie_details->cookie_list,
146 [](const network::mojom::CookieOrLineWithAccessResultPtr&
147 cookie_and_access_result) {
148 return cookie_and_access_result->access_result.status
Dylan Cutler0ed68252022-10-17 16:54:33149 .ExcludedByUserPreferences();
Jayson Adams295430202021-07-27 01:07:57150 });
151 blocked->cookie_list.reserve(blocked_count);
Alexander Timin1cc31f42020-05-12 16:26:01152
Jayson Adams295430202021-07-27 01:07:57153 for (const auto& cookie_and_access_result : cookie_details->cookie_list) {
Dylan Cutler0ed68252022-10-17 16:54:33154 if (cookie_and_access_result->access_result.status
155 .ExcludedByUserPreferences()) {
Jayson Adams295430202021-07-27 01:07:57156 blocked->cookie_list.emplace_back(
cfredric76b2d222021-01-27 20:12:04157 std::move(cookie_and_access_result->cookie_or_line->get_cookie()));
158 } else if (cookie_and_access_result->access_result.status.IsInclude()) {
Jayson Adams295430202021-07-27 01:07:57159 allowed->cookie_list.emplace_back(
cfredric76b2d222021-01-27 20:12:04160 std::move(cookie_and_access_result->cookie_or_line->get_cookie()));
Alexander Timin1cc31f42020-05-12 16:26:01161 }
162 }
163}
164
cfredrica5fb0982021-01-09 00:18:01165void EmitCookieWarningsAndMetrics(
Alexander Timin1cc31f42020-05-12 16:26:01166 RenderFrameHostImpl* rfh,
167 const network::mojom::CookieAccessDetailsPtr& cookie_details) {
168 RenderFrameHostImpl* root_frame_host = rfh->GetMainFrame();
169
Sreeja Kamishettye49854f82021-06-02 00:52:03170 if (!root_frame_host->IsActive())
Alexander Timin1cc31f42020-05-12 16:26:01171 return;
172
173 bool samesite_treated_as_lax_cookies = false;
174 bool samesite_none_insecure_cookies = false;
Alexander Timin1cc31f42020-05-12 16:26:01175 bool breaking_context_downgrade = false;
Lily Chenc4423c02021-03-11 16:02:02176 bool lax_allow_unsafe_cookies = false;
Alexander Timin1cc31f42020-05-12 16:26:01177
Lily Chen2db3a422021-07-20 18:02:25178 bool samesite_cookie_inclusion_changed_by_cross_site_redirect = false;
179
Dylan Cutler8d5f8912022-03-04 17:39:19180 bool partitioned_cookies_exist = false;
181
Ari Chivukula5f21c112022-04-26 19:23:34182 bool cookie_has_not_been_refreshed_in_201_to_300_days = false;
183 bool cookie_has_not_been_refreshed_in_301_to_350_days = false;
184 bool cookie_has_not_been_refreshed_in_351_to_400_days = false;
185
Johann Hofmanne5764d12022-07-13 23:06:28186 bool cookie_has_domain_non_ascii = false;
187
cfredric76b2d222021-01-27 20:12:04188 for (const network::mojom::CookieOrLineWithAccessResultPtr& cookie :
Alexander Timin1cc31f42020-05-12 16:26:01189 cookie_details->cookie_list) {
Johann Hofmann975c0b42022-08-02 21:05:51190 const net::CookieInclusionStatus& status = cookie->access_result.status;
191 if (ShouldReportDevToolsIssueForStatus(status)) {
Juba Borgohainc93969e2022-02-25 21:56:12192 devtools_instrumentation::ReportCookieIssue(
Simon Zünd657178e2021-05-27 06:19:55193 root_frame_host, cookie, cookie_details->url,
194 cookie_details->site_for_cookies,
195 cookie_details->type == CookieAccessDetails::Type::kRead
Juba Borgohainc93969e2022-02-25 21:56:12196 ? blink::mojom::CookieOperation::kReadCookie
197 : blink::mojom::CookieOperation::kSetCookie,
Simon Zünd657178e2021-05-27 06:19:55198 cookie_details->devtools_request_id);
199 }
200
cfredric76b2d222021-01-27 20:12:04201 if (cookie->access_result.status.ShouldWarn()) {
Lily Chen9de4065b2020-06-24 20:18:47202 samesite_treated_as_lax_cookies =
203 samesite_treated_as_lax_cookies ||
cfredrica5fb0982021-01-09 00:18:01204 status.HasWarningReason(
Jihwan Marc Kim3e132f12020-05-20 17:33:19205 net::CookieInclusionStatus::
Lily Chen9de4065b2020-06-24 20:18:47206 WARN_SAMESITE_UNSPECIFIED_CROSS_SITE_CONTEXT) ||
cfredrica5fb0982021-01-09 00:18:01207 status.HasWarningReason(
Jihwan Marc Kim3e132f12020-05-20 17:33:19208 net::CookieInclusionStatus::
Lily Chen9de4065b2020-06-24 20:18:47209 WARN_SAMESITE_UNSPECIFIED_LAX_ALLOW_UNSAFE);
Alexander Timin1cc31f42020-05-12 16:26:01210
Lily Chen9de4065b2020-06-24 20:18:47211 samesite_none_insecure_cookies =
212 samesite_none_insecure_cookies ||
cfredrica5fb0982021-01-09 00:18:01213 status.HasWarningReason(
Lily Chen9de4065b2020-06-24 20:18:47214 net::CookieInclusionStatus::WARN_SAMESITE_NONE_INSECURE);
215
Lily Chenc4423c02021-03-11 16:02:02216 lax_allow_unsafe_cookies =
217 lax_allow_unsafe_cookies ||
218 status.HasWarningReason(
219 net::CookieInclusionStatus::
220 WARN_SAMESITE_UNSPECIFIED_LAX_ALLOW_UNSAFE);
221
Lily Chen2db3a422021-07-20 18:02:25222 samesite_cookie_inclusion_changed_by_cross_site_redirect =
223 samesite_cookie_inclusion_changed_by_cross_site_redirect ||
224 status.HasWarningReason(
225 net::CookieInclusionStatus::
226 WARN_CROSS_SITE_REDIRECT_DOWNGRADE_CHANGES_INCLUSION);
Alexander Timin1cc31f42020-05-12 16:26:01227 }
Alexander Timin1cc31f42020-05-12 16:26:01228
Johann Hofmann975c0b42022-08-02 21:05:51229 cookie_has_domain_non_ascii =
230 cookie_has_domain_non_ascii ||
231 status.HasWarningReason(
232 net::CookieInclusionStatus::WARN_DOMAIN_NON_ASCII) ||
233 status.HasExclusionReason(
234 net::CookieInclusionStatus::EXCLUDE_DOMAIN_NON_ASCII);
235
Dylan Cutler8d5f8912022-03-04 17:39:19236 partitioned_cookies_exist =
237 partitioned_cookies_exist ||
238 (cookie->cookie_or_line->is_cookie() &&
Dylan Cutler051411b42022-07-12 22:20:07239 cookie->cookie_or_line->get_cookie().IsPartitioned() &&
240 // Ignore nonced partition keys since this metric is meant to track
241 // usage of the Partitioned attribute.
242 !cookie->cookie_or_line->get_cookie().PartitionKey()->nonce());
Dylan Cutler8d5f8912022-03-04 17:39:19243
selya27d093e2023-08-02 22:20:00244 RecordPartitionedCookieUseUKM(rfh, partitioned_cookies_exist);
245
Ayu Ishii2e3998902020-07-14 18:22:30246 breaking_context_downgrade =
247 breaking_context_downgrade ||
sbingler870b63f2023-04-25 16:47:58248 cookie->access_result.status.HasSchemefulDowngradeWarning();
Alexander Timin1cc31f42020-05-12 16:26:01249
sbingler870b63f2023-04-25 16:47:58250 if (cookie->access_result.status.HasSchemefulDowngradeWarning()) {
251 // Unlike with UMA, do not record cookies that have no schemeful downgrade
252 // warning.
253 RecordSchemefulContextDowngradeUKM(rfh, cookie_details->type,
254 cookie->access_result.status,
255 cookie_details->url);
256 }
257
258 if (status.HasWarningReason(
259 net::CookieInclusionStatus::
260 WARN_CROSS_SITE_REDIRECT_DOWNGRADE_CHANGES_INCLUSION) &&
261 cookie->cookie_or_line->is_cookie()) {
262 RecordRedirectContextDowngradeUKM(rfh, cookie_details->type,
263 cookie->cookie_or_line->get_cookie(),
264 cookie_details->url);
Alexander Timin1cc31f42020-05-12 16:26:01265 }
Ari Chivukula5f21c112022-04-26 19:23:34266
267 // In order to anticipate the potential effects of the expiry limit in
268 // rfc6265bis, we need to check how long it's been since the cookie was
269 // refreshed (if LastUpdateDate is populated). These three buckets were
270 // picked so we could engage sites with some granularity around urgency.
271 // We ignore the space under 200 days as these cookies are not at risk
272 // of expiring and we ignore the space over 400 days as these cookies
273 // have already expired. Metrics will take 200 days from M103 to populate.
274 base::Time last_update_date =
275 cookie->cookie_or_line->is_cookie()
276 ? cookie->cookie_or_line->get_cookie().LastUpdateDate()
277 : base::Time();
278 if (!last_update_date.is_null()) {
279 int days_since_refresh = (base::Time::Now() - last_update_date).InDays();
280 cookie_has_not_been_refreshed_in_201_to_300_days |=
281 days_since_refresh > 200 && days_since_refresh <= 300;
282 cookie_has_not_been_refreshed_in_301_to_350_days |=
283 days_since_refresh > 300 && days_since_refresh <= 350;
284 cookie_has_not_been_refreshed_in_351_to_400_days |=
285 days_since_refresh > 350 && days_since_refresh <= 400;
286 }
Alexander Timin1cc31f42020-05-12 16:26:01287 }
288
Alexander Timin1cc31f42020-05-12 16:26:01289 if (samesite_treated_as_lax_cookies) {
290 GetContentClient()->browser()->LogWebFeatureForCurrentPage(
291 rfh, blink::mojom::WebFeature::kCookieNoSameSite);
292 }
293
294 if (samesite_none_insecure_cookies) {
295 GetContentClient()->browser()->LogWebFeatureForCurrentPage(
296 rfh, blink::mojom::WebFeature::kCookieInsecureAndSameSiteNone);
297 }
298
299 if (breaking_context_downgrade) {
300 GetContentClient()->browser()->LogWebFeatureForCurrentPage(
301 rfh, blink::mojom::WebFeature::kSchemefulSameSiteContextDowngrade);
302 }
cfredrica5fb0982021-01-09 00:18:01303
Lily Chenc4423c02021-03-11 16:02:02304 if (lax_allow_unsafe_cookies) {
305 GetContentClient()->browser()->LogWebFeatureForCurrentPage(
306 rfh, blink::mojom::WebFeature::kLaxAllowingUnsafeCookies);
307 }
308
Lily Chen2db3a422021-07-20 18:02:25309 if (samesite_cookie_inclusion_changed_by_cross_site_redirect) {
310 GetContentClient()->browser()->LogWebFeatureForCurrentPage(
311 rfh, blink::mojom::WebFeature::
312 kSameSiteCookieInclusionChangedByCrossSiteRedirect);
313 }
Dylan Cutler8d5f8912022-03-04 17:39:19314
315 if (partitioned_cookies_exist) {
316 GetContentClient()->browser()->LogWebFeatureForCurrentPage(
317 rfh, blink::mojom::WebFeature::kPartitionedCookies);
318 }
Ari Chivukula5f21c112022-04-26 19:23:34319
320 if (cookie_has_not_been_refreshed_in_201_to_300_days) {
321 GetContentClient()->browser()->LogWebFeatureForCurrentPage(
322 rfh,
323 blink::mojom::WebFeature::kCookieHasNotBeenRefreshedIn201To300Days);
324 }
325
326 if (cookie_has_not_been_refreshed_in_301_to_350_days) {
327 GetContentClient()->browser()->LogWebFeatureForCurrentPage(
328 rfh,
329 blink::mojom::WebFeature::kCookieHasNotBeenRefreshedIn301To350Days);
330 }
331
332 if (cookie_has_not_been_refreshed_in_351_to_400_days) {
333 GetContentClient()->browser()->LogWebFeatureForCurrentPage(
334 rfh,
335 blink::mojom::WebFeature::kCookieHasNotBeenRefreshedIn351To400Days);
336 }
Johann Hofmanne5764d12022-07-13 23:06:28337
338 if (cookie_has_domain_non_ascii) {
339 GetContentClient()->browser()->LogWebFeatureForCurrentPage(
340 rfh, blink::mojom::WebFeature::kCookieDomainNonASCII);
341 }
Alexander Timin1cc31f42020-05-12 16:26:01342}
343
344} // namespace content