1 // Copyright 2024 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "net/cookies/cookie_base.h"
6
7 #include "base/containers/contains.h"
8 #include "base/feature_list.h"
9 #include "base/strings/strcat.h"
10 #include "net/base/features.h"
11 #include "net/cookies/cookie_inclusion_status.h"
12 #include "net/cookies/cookie_util.h"
13
14 namespace net {
15
16 namespace {
17
18 // Captures Strict -> Lax context downgrade with Strict cookie
IsBreakingStrictToLaxDowngrade(CookieOptions::SameSiteCookieContext::ContextType context,CookieOptions::SameSiteCookieContext::ContextType schemeful_context,CookieEffectiveSameSite effective_same_site,bool is_cookie_being_set)19 bool IsBreakingStrictToLaxDowngrade(
20 CookieOptions::SameSiteCookieContext::ContextType context,
21 CookieOptions::SameSiteCookieContext::ContextType schemeful_context,
22 CookieEffectiveSameSite effective_same_site,
23 bool is_cookie_being_set) {
24 if (context ==
25 CookieOptions::SameSiteCookieContext::ContextType::SAME_SITE_STRICT &&
26 schemeful_context ==
27 CookieOptions::SameSiteCookieContext::ContextType::SAME_SITE_LAX &&
28 effective_same_site == CookieEffectiveSameSite::STRICT_MODE) {
29 // This downgrade only applies when a SameSite=Strict cookie is being sent.
30 // A Strict -> Lax downgrade will not affect a Strict cookie which is being
31 // set because it will be set in either context.
32 return !is_cookie_being_set;
33 }
34
35 return false;
36 }
37
38 // Captures Strict -> Cross-site context downgrade with {Strict, Lax} cookie
39 // Captures Strict -> Lax Unsafe context downgrade with {Strict, Lax} cookie.
40 // This is treated as a cross-site downgrade due to the Lax Unsafe context
41 // behaving like cross-site.
IsBreakingStrictToCrossDowngrade(CookieOptions::SameSiteCookieContext::ContextType context,CookieOptions::SameSiteCookieContext::ContextType schemeful_context,CookieEffectiveSameSite effective_same_site)42 bool IsBreakingStrictToCrossDowngrade(
43 CookieOptions::SameSiteCookieContext::ContextType context,
44 CookieOptions::SameSiteCookieContext::ContextType schemeful_context,
45 CookieEffectiveSameSite effective_same_site) {
46 bool breaking_schemeful_context =
47 schemeful_context ==
48 CookieOptions::SameSiteCookieContext::ContextType::CROSS_SITE ||
49 schemeful_context == CookieOptions::SameSiteCookieContext::ContextType::
50 SAME_SITE_LAX_METHOD_UNSAFE;
51
52 bool strict_lax_enforcement =
53 effective_same_site == CookieEffectiveSameSite::STRICT_MODE ||
54 effective_same_site == CookieEffectiveSameSite::LAX_MODE ||
55 // Treat LAX_MODE_ALLOW_UNSAFE the same as LAX_MODE for the purposes of
56 // our SameSite enforcement check.
57 effective_same_site == CookieEffectiveSameSite::LAX_MODE_ALLOW_UNSAFE;
58
59 if (context ==
60 CookieOptions::SameSiteCookieContext::ContextType::SAME_SITE_STRICT &&
61 breaking_schemeful_context && strict_lax_enforcement) {
62 return true;
63 }
64
65 return false;
66 }
67
68 // Captures Lax -> Cross context downgrade with {Strict, Lax} cookies.
69 // Ignores Lax Unsafe context.
IsBreakingLaxToCrossDowngrade(CookieOptions::SameSiteCookieContext::ContextType context,CookieOptions::SameSiteCookieContext::ContextType schemeful_context,CookieEffectiveSameSite effective_same_site,bool is_cookie_being_set)70 bool IsBreakingLaxToCrossDowngrade(
71 CookieOptions::SameSiteCookieContext::ContextType context,
72 CookieOptions::SameSiteCookieContext::ContextType schemeful_context,
73 CookieEffectiveSameSite effective_same_site,
74 bool is_cookie_being_set) {
75 bool lax_enforcement =
76 effective_same_site == CookieEffectiveSameSite::LAX_MODE ||
77 // Treat LAX_MODE_ALLOW_UNSAFE the same as LAX_MODE for the purposes of
78 // our SameSite enforcement check.
79 effective_same_site == CookieEffectiveSameSite::LAX_MODE_ALLOW_UNSAFE;
80
81 if (context ==
82 CookieOptions::SameSiteCookieContext::ContextType::SAME_SITE_LAX &&
83 schemeful_context ==
84 CookieOptions::SameSiteCookieContext::ContextType::CROSS_SITE) {
85 // For SameSite=Strict cookies this downgrade only applies when it is being
86 // set. A Lax -> Cross downgrade will not affect a Strict cookie which is
87 // being sent because it wouldn't be sent in either context.
88 return effective_same_site == CookieEffectiveSameSite::STRICT_MODE
89 ? is_cookie_being_set
90 : lax_enforcement;
91 }
92
93 return false;
94 }
95
ApplySameSiteCookieWarningToStatus(CookieSameSite samesite,CookieEffectiveSameSite effective_samesite,bool is_secure,const CookieOptions::SameSiteCookieContext & same_site_context,CookieInclusionStatus * status,bool is_cookie_being_set)96 void ApplySameSiteCookieWarningToStatus(
97 CookieSameSite samesite,
98 CookieEffectiveSameSite effective_samesite,
99 bool is_secure,
100 const CookieOptions::SameSiteCookieContext& same_site_context,
101 CookieInclusionStatus* status,
102 bool is_cookie_being_set) {
103 if (samesite == CookieSameSite::UNSPECIFIED &&
104 same_site_context.GetContextForCookieInclusion() <
105 CookieOptions::SameSiteCookieContext::ContextType::SAME_SITE_LAX) {
106 status->AddWarningReason(
107 CookieInclusionStatus::WARN_SAMESITE_UNSPECIFIED_CROSS_SITE_CONTEXT);
108 }
109 if (effective_samesite == CookieEffectiveSameSite::LAX_MODE_ALLOW_UNSAFE &&
110 same_site_context.GetContextForCookieInclusion() ==
111 CookieOptions::SameSiteCookieContext::ContextType::
112 SAME_SITE_LAX_METHOD_UNSAFE) {
113 // This warning is more specific so remove the previous, more general,
114 // warning.
115 status->RemoveWarningReason(
116 CookieInclusionStatus::WARN_SAMESITE_UNSPECIFIED_CROSS_SITE_CONTEXT);
117 status->AddWarningReason(
118 CookieInclusionStatus::WARN_SAMESITE_UNSPECIFIED_LAX_ALLOW_UNSAFE);
119 }
120 if (samesite == CookieSameSite::NO_RESTRICTION && !is_secure) {
121 status->AddWarningReason(
122 CookieInclusionStatus::WARN_SAMESITE_NONE_INSECURE);
123 }
124
125 // Add a warning if the cookie would be accessible in
126 // |same_site_context|::context but not in
127 // |same_site_context|::schemeful_context.
128 if (IsBreakingStrictToLaxDowngrade(same_site_context.context(),
129 same_site_context.schemeful_context(),
130 effective_samesite, is_cookie_being_set)) {
131 status->AddWarningReason(
132 CookieInclusionStatus::WARN_STRICT_LAX_DOWNGRADE_STRICT_SAMESITE);
133 } else if (IsBreakingStrictToCrossDowngrade(
134 same_site_context.context(),
135 same_site_context.schemeful_context(), effective_samesite)) {
136 // Which warning to apply depends on the SameSite value.
137 if (effective_samesite == CookieEffectiveSameSite::STRICT_MODE) {
138 status->AddWarningReason(
139 CookieInclusionStatus::WARN_STRICT_CROSS_DOWNGRADE_STRICT_SAMESITE);
140 } else {
141 // LAX_MODE or LAX_MODE_ALLOW_UNSAFE.
142 status->AddWarningReason(
143 CookieInclusionStatus::WARN_STRICT_CROSS_DOWNGRADE_LAX_SAMESITE);
144 }
145
146 } else if (IsBreakingLaxToCrossDowngrade(
147 same_site_context.context(),
148 same_site_context.schemeful_context(), effective_samesite,
149 is_cookie_being_set)) {
150 // Which warning to apply depends on the SameSite value.
151 if (effective_samesite == CookieEffectiveSameSite::STRICT_MODE) {
152 status->AddWarningReason(
153 CookieInclusionStatus::WARN_LAX_CROSS_DOWNGRADE_STRICT_SAMESITE);
154 } else {
155 // LAX_MODE or LAX_MODE_ALLOW_UNSAFE.
156 // This warning applies to both set/send.
157 status->AddWarningReason(
158 CookieInclusionStatus::WARN_LAX_CROSS_DOWNGRADE_LAX_SAMESITE);
159 }
160 }
161
162 // Apply warning for whether inclusion was changed by considering redirects
163 // for the SameSite context calculation. This does not look at the actual
164 // inclusion or exclusion, but only at whether the inclusion differs between
165 // considering redirects and not.
166 using ContextDowngradeType = CookieOptions::SameSiteCookieContext::
167 ContextMetadata::ContextDowngradeType;
168 const auto& metadata = same_site_context.GetMetadataForCurrentSchemefulMode();
169 bool apply_cross_site_redirect_downgrade_warning = false;
170 switch (effective_samesite) {
171 case CookieEffectiveSameSite::STRICT_MODE:
172 // Strict contexts are all normalized to lax for cookie writes, so a
173 // strict-to-{lax,cross} downgrade cannot occur for response cookies.
174 apply_cross_site_redirect_downgrade_warning =
175 is_cookie_being_set ? metadata.cross_site_redirect_downgrade ==
176 ContextDowngradeType::kLaxToCross
177 : (metadata.cross_site_redirect_downgrade ==
178 ContextDowngradeType::kStrictToLax ||
179 metadata.cross_site_redirect_downgrade ==
180 ContextDowngradeType::kStrictToCross);
181 break;
182 case CookieEffectiveSameSite::LAX_MODE:
183 case CookieEffectiveSameSite::LAX_MODE_ALLOW_UNSAFE:
184 // Note that a lax-to-cross downgrade can only happen for response
185 // cookies, because a laxly same-site context only happens for a safe
186 // top-level cross-site request, which cannot be downgraded due to a
187 // cross-site redirect to a non-top-level or unsafe cross-site request.
188 apply_cross_site_redirect_downgrade_warning =
189 metadata.cross_site_redirect_downgrade ==
190 (is_cookie_being_set ? ContextDowngradeType::kLaxToCross
191 : ContextDowngradeType::kStrictToCross);
192 break;
193 default:
194 break;
195 }
196 if (apply_cross_site_redirect_downgrade_warning) {
197 status->AddWarningReason(
198 CookieInclusionStatus::
199 WARN_CROSS_SITE_REDIRECT_DOWNGRADE_CHANGES_INCLUSION);
200 }
201
202 // If there are reasons to exclude the cookie other than SameSite, don't warn
203 // about the cookie at all.
204 status->MaybeClearSameSiteWarning();
205 }
206
207 } // namespace
208
IncludeForRequestURL(const GURL & url,const CookieOptions & options,const CookieAccessParams & params) const209 CookieAccessResult CookieBase::IncludeForRequestURL(
210 const GURL& url,
211 const CookieOptions& options,
212 const CookieAccessParams& params) const {
213 CookieInclusionStatus status;
214 // Filter out HttpOnly cookies, per options.
215 if (options.exclude_httponly() && IsHttpOnly()) {
216 status.AddExclusionReason(CookieInclusionStatus::EXCLUDE_HTTP_ONLY);
217 }
218 // Secure cookies should not be included in requests for URLs with an
219 // insecure scheme, unless it is a localhost url, or the CookieAccessDelegate
220 // otherwise denotes them as trustworthy
221 // (`delegate_treats_url_as_trustworthy`).
222 bool is_allowed_to_access_secure_cookies = false;
223 CookieAccessScheme cookie_access_scheme =
224 cookie_util::ProvisionalAccessScheme(url);
225 if (cookie_access_scheme == CookieAccessScheme::kNonCryptographic &&
226 params.delegate_treats_url_as_trustworthy) {
227 cookie_access_scheme = CookieAccessScheme::kTrustworthy;
228 }
229 switch (cookie_access_scheme) {
230 case CookieAccessScheme::kNonCryptographic:
231 if (SecureAttribute()) {
232 status.AddExclusionReason(CookieInclusionStatus::EXCLUDE_SECURE_ONLY);
233 }
234 break;
235 case CookieAccessScheme::kTrustworthy:
236 is_allowed_to_access_secure_cookies = true;
237 if (SecureAttribute() ||
238 (cookie_util::IsSchemeBoundCookiesEnabled() &&
239 source_scheme_ == CookieSourceScheme::kSecure)) {
240 status.AddWarningReason(
241 CookieInclusionStatus::
242 WARN_SECURE_ACCESS_GRANTED_NON_CRYPTOGRAPHIC);
243 }
244 break;
245 case CookieAccessScheme::kCryptographic:
246 is_allowed_to_access_secure_cookies = true;
247 break;
248 }
249
250 // For the following two sections we're checking to see if a cookie's
251 // `source_scheme_` and `source_port_` match that of the url's. In most cases
252 // this is a direct comparison but it does get a bit more complicated when
253 // trustworthy origins are taken into accounts. Note that here, a kTrustworthy
254 // url must have a non-secure scheme (http) because otherwise it'd be a
255 // kCryptographic url.
256 //
257 // Trustworthy origins are allowed to both secure and non-secure cookies. This
258 // means that we'll match source_scheme_ for both their usual kNonSecure as
259 // well as KSecure. For source_port_ we'll match per usual as well as any 443
260 // ports, since those are the default values for secure cookies and we still
261 // want to be able to access them.
262
263 // A cookie with a source scheme of kSecure shouldn't be accessible by
264 // kNonCryptographic urls. But we can skip adding a status if the cookie is
265 // already blocked due to the `Secure` attribute.
266 if (source_scheme_ == CookieSourceScheme::kSecure &&
267 cookie_access_scheme == CookieAccessScheme::kNonCryptographic &&
268 !status.HasExclusionReason(CookieInclusionStatus::EXCLUDE_SECURE_ONLY)) {
269 if (cookie_util::IsSchemeBoundCookiesEnabled()) {
270 status.AddExclusionReason(CookieInclusionStatus::EXCLUDE_SCHEME_MISMATCH);
271 } else {
272 status.AddWarningReason(CookieInclusionStatus::WARN_SCHEME_MISMATCH);
273 }
274 }
275 // A cookie with a source scheme of kNonSecure shouldn't be accessible by
276 // kCryptographic urls.
277 else if (source_scheme_ == CookieSourceScheme::kNonSecure &&
278 cookie_access_scheme == CookieAccessScheme::kCryptographic) {
279 if (cookie_util::IsSchemeBoundCookiesEnabled()) {
280 status.AddExclusionReason(CookieInclusionStatus::EXCLUDE_SCHEME_MISMATCH);
281 } else {
282 status.AddWarningReason(CookieInclusionStatus::WARN_SCHEME_MISMATCH);
283 }
284 }
285 // Else, the cookie has a source scheme of kUnset or the access scheme is
286 // kTrustworthy. Neither of which will block the cookie.
287
288 int url_port = url.EffectiveIntPort();
289 CHECK(url_port != url::PORT_INVALID);
290 // The cookie's source port either must match the url's port, be
291 // PORT_UNSPECIFIED, or the cookie must be a domain cookie.
292 bool port_matches = url_port == source_port_ ||
293 source_port_ == url::PORT_UNSPECIFIED || IsDomainCookie();
294
295 // Or if the url is trustworthy, we'll also match 443 (in order to get secure
296 // cookies).
297 bool trustworthy_and_443 =
298 cookie_access_scheme == CookieAccessScheme::kTrustworthy &&
299 source_port_ == 443;
300 if (!port_matches && !trustworthy_and_443) {
301 if (cookie_util::IsPortBoundCookiesEnabled()) {
302 status.AddExclusionReason(CookieInclusionStatus::EXCLUDE_PORT_MISMATCH);
303 } else {
304 status.AddWarningReason(CookieInclusionStatus::WARN_PORT_MISMATCH);
305 }
306 }
307
308 // Don't include cookies for requests that don't apply to the cookie domain.
309 if (!IsDomainMatch(url.host())) {
310 status.AddExclusionReason(CookieInclusionStatus::EXCLUDE_DOMAIN_MISMATCH);
311 }
312 // Don't include cookies for requests with a url path that does not path
313 // match the cookie-path.
314 if (!IsOnPath(url.path())) {
315 status.AddExclusionReason(CookieInclusionStatus::EXCLUDE_NOT_ON_PATH);
316 }
317
318 // For LEGACY cookies we should always return the schemeless context,
319 // otherwise let GetContextForCookieInclusion() decide.
320 const CookieOptions::SameSiteCookieContext::ContextType
321 cookie_inclusion_context =
322 params.access_semantics == CookieAccessSemantics::LEGACY
323 ? options.same_site_cookie_context().context()
324 : options.same_site_cookie_context()
325 .GetContextForCookieInclusion();
326
327 // Don't include same-site cookies for cross-site requests.
328 CookieEffectiveSameSite effective_same_site =
329 GetEffectiveSameSite(params.access_semantics);
330 DCHECK(effective_same_site != CookieEffectiveSameSite::UNDEFINED);
331
332 switch (effective_same_site) {
333 case CookieEffectiveSameSite::STRICT_MODE:
334 if (cookie_inclusion_context <
335 CookieOptions::SameSiteCookieContext::ContextType::SAME_SITE_STRICT) {
336 status.AddExclusionReason(
337 CookieInclusionStatus::EXCLUDE_SAMESITE_STRICT);
338 }
339 break;
340 case CookieEffectiveSameSite::LAX_MODE:
341 if (cookie_inclusion_context <
342 CookieOptions::SameSiteCookieContext::ContextType::SAME_SITE_LAX) {
343 status.AddExclusionReason(
344 (SameSite() == CookieSameSite::UNSPECIFIED)
345 ? CookieInclusionStatus::
346 EXCLUDE_SAMESITE_UNSPECIFIED_TREATED_AS_LAX
347 : CookieInclusionStatus::EXCLUDE_SAMESITE_LAX);
348 }
349 break;
350 // TODO(crbug.com/990439): Add a browsertest for this behavior.
351 case CookieEffectiveSameSite::LAX_MODE_ALLOW_UNSAFE:
352 DCHECK(SameSite() == CookieSameSite::UNSPECIFIED);
353 if (cookie_inclusion_context <
354 CookieOptions::SameSiteCookieContext::ContextType::
355 SAME_SITE_LAX_METHOD_UNSAFE) {
356 // TODO(chlily): Do we need a separate CookieInclusionStatus for this?
357 status.AddExclusionReason(
358 CookieInclusionStatus::EXCLUDE_SAMESITE_UNSPECIFIED_TREATED_AS_LAX);
359 }
360 break;
361 default:
362 break;
363 }
364
365 // Unless legacy access semantics are in effect, SameSite=None cookies without
366 // the Secure attribute should be ignored. This can apply to cookies which
367 // were created before "SameSite=None requires Secure" was enabled (as
368 // SameSite=None insecure cookies cannot be set while the options are on).
369 if (params.access_semantics != CookieAccessSemantics::LEGACY &&
370 SameSite() == CookieSameSite::NO_RESTRICTION && !SecureAttribute()) {
371 status.AddExclusionReason(
372 CookieInclusionStatus::EXCLUDE_SAMESITE_NONE_INSECURE);
373 }
374
375 ApplySameSiteCookieWarningToStatus(SameSite(), effective_same_site,
376 SecureAttribute(),
377 options.same_site_cookie_context(),
378 &status, false /* is_cookie_being_set */);
379
380 CookieAccessResult result{effective_same_site, status,
381 params.access_semantics,
382 is_allowed_to_access_secure_cookies};
383
384 PostIncludeForRequestURL(result, options, cookie_inclusion_context);
385
386 return result;
387 }
388
IsSetPermittedInContext(const GURL & source_url,const CookieOptions & options,const CookieAccessParams & params,const std::vector<std::string> & cookieable_schemes,const std::optional<CookieAccessResult> & cookie_access_result) const389 CookieAccessResult CookieBase::IsSetPermittedInContext(
390 const GURL& source_url,
391 const CookieOptions& options,
392 const CookieAccessParams& params,
393 const std::vector<std::string>& cookieable_schemes,
394 const std::optional<CookieAccessResult>& cookie_access_result) const {
395 CookieAccessResult access_result;
396 if (cookie_access_result) {
397 access_result = *cookie_access_result;
398 }
399
400 if (!base::Contains(cookieable_schemes, source_url.scheme())) {
401 access_result.status.AddExclusionReason(
402 CookieInclusionStatus::EXCLUDE_NONCOOKIEABLE_SCHEME);
403 }
404
405 CookieAccessScheme access_scheme =
406 cookie_util::ProvisionalAccessScheme(source_url);
407 if (access_scheme == CookieAccessScheme::kNonCryptographic &&
408 params.delegate_treats_url_as_trustworthy) {
409 access_scheme = CookieAccessScheme::kTrustworthy;
410 }
411
412 switch (access_scheme) {
413 case CookieAccessScheme::kNonCryptographic:
414 access_result.is_allowed_to_access_secure_cookies = false;
415 if (SecureAttribute()) {
416 access_result.status.AddExclusionReason(
417 CookieInclusionStatus::EXCLUDE_SECURE_ONLY);
418 }
419 break;
420
421 case CookieAccessScheme::kCryptographic:
422 // All cool!
423 access_result.is_allowed_to_access_secure_cookies = true;
424 break;
425
426 case CookieAccessScheme::kTrustworthy:
427 access_result.is_allowed_to_access_secure_cookies = true;
428 if (SecureAttribute()) {
429 // OK, but want people aware of this.
430 // Note, we also want to apply this warning to cookies whose source
431 // scheme is kSecure but are set by non-cryptographic (but trustworthy)
432 // urls. Helpfully, since those cookies only get a kSecure source scheme
433 // when they also specify "Secure" this if statement will already apply
434 // to them.
435 access_result.status.AddWarningReason(
436 CookieInclusionStatus::
437 WARN_SECURE_ACCESS_GRANTED_NON_CRYPTOGRAPHIC);
438 }
439 break;
440 }
441
442 access_result.access_semantics = params.access_semantics;
443 if (options.exclude_httponly() && IsHttpOnly()) {
444 DVLOG(net::cookie_util::kVlogSetCookies)
445 << "HttpOnly cookie not permitted in script context.";
446 access_result.status.AddExclusionReason(
447 CookieInclusionStatus::EXCLUDE_HTTP_ONLY);
448 }
449
450 // Unless legacy access semantics are in effect, SameSite=None cookies without
451 // the Secure attribute will be rejected.
452 if (params.access_semantics != CookieAccessSemantics::LEGACY &&
453 SameSite() == CookieSameSite::NO_RESTRICTION && !SecureAttribute()) {
454 DVLOG(net::cookie_util::kVlogSetCookies)
455 << "SetCookie() rejecting insecure cookie with SameSite=None.";
456 access_result.status.AddExclusionReason(
457 CookieInclusionStatus::EXCLUDE_SAMESITE_NONE_INSECURE);
458 }
459
460 // For LEGACY cookies we should always return the schemeless context,
461 // otherwise let GetContextForCookieInclusion() decide.
462 CookieOptions::SameSiteCookieContext::ContextType cookie_inclusion_context =
463 params.access_semantics == CookieAccessSemantics::LEGACY
464 ? options.same_site_cookie_context().context()
465 : options.same_site_cookie_context().GetContextForCookieInclusion();
466
467 access_result.effective_same_site =
468 GetEffectiveSameSite(params.access_semantics);
469 DCHECK(access_result.effective_same_site !=
470 CookieEffectiveSameSite::UNDEFINED);
471 switch (access_result.effective_same_site) {
472 case CookieEffectiveSameSite::STRICT_MODE:
473 // This intentionally checks for `< SAME_SITE_LAX`, as we allow
474 // `SameSite=Strict` cookies to be set for top-level navigations that
475 // qualify for receipt of `SameSite=Lax` cookies.
476 if (cookie_inclusion_context <
477 CookieOptions::SameSiteCookieContext::ContextType::SAME_SITE_LAX) {
478 DVLOG(net::cookie_util::kVlogSetCookies)
479 << "Trying to set a `SameSite=Strict` cookie from a "
480 "cross-site URL.";
481 access_result.status.AddExclusionReason(
482 CookieInclusionStatus::EXCLUDE_SAMESITE_STRICT);
483 }
484 break;
485 case CookieEffectiveSameSite::LAX_MODE:
486 case CookieEffectiveSameSite::LAX_MODE_ALLOW_UNSAFE:
487 if (cookie_inclusion_context <
488 CookieOptions::SameSiteCookieContext::ContextType::SAME_SITE_LAX) {
489 if (SameSite() == CookieSameSite::UNSPECIFIED) {
490 DVLOG(net::cookie_util::kVlogSetCookies)
491 << "Cookies with no known SameSite attribute being treated as "
492 "lax; attempt to set from a cross-site URL denied.";
493 access_result.status.AddExclusionReason(
494 CookieInclusionStatus::
495 EXCLUDE_SAMESITE_UNSPECIFIED_TREATED_AS_LAX);
496 } else {
497 DVLOG(net::cookie_util::kVlogSetCookies)
498 << "Trying to set a `SameSite=Lax` cookie from a cross-site URL.";
499 access_result.status.AddExclusionReason(
500 CookieInclusionStatus::EXCLUDE_SAMESITE_LAX);
501 }
502 }
503 break;
504 default:
505 break;
506 }
507
508 ApplySameSiteCookieWarningToStatus(
509 SameSite(), access_result.effective_same_site, SecureAttribute(),
510 options.same_site_cookie_context(), &access_result.status,
511 true /* is_cookie_being_set */);
512
513 PostIsSetPermittedInContext(access_result, options);
514
515 return access_result;
516 }
517
IsOnPath(const std::string & url_path) const518 bool CookieBase::IsOnPath(const std::string& url_path) const {
519 return cookie_util::IsOnPath(path_, url_path);
520 }
521
IsDomainMatch(const std::string & host) const522 bool CookieBase::IsDomainMatch(const std::string& host) const {
523 return cookie_util::IsDomainMatch(domain_, host);
524 }
525
IsSecure() const526 bool CookieBase::IsSecure() const {
527 return SecureAttribute() || (cookie_util::IsSchemeBoundCookiesEnabled() &&
528 source_scheme_ == CookieSourceScheme::kSecure);
529 }
530
IsFirstPartyPartitioned() const531 bool CookieBase::IsFirstPartyPartitioned() const {
532 return IsPartitioned() && !CookiePartitionKey::HasNonce(partition_key_) &&
533 SchemefulSite(GURL(
534 base::StrCat({url::kHttpsScheme, url::kStandardSchemeSeparator,
535 DomainWithoutDot()}))) == partition_key_->site();
536 }
537
IsThirdPartyPartitioned() const538 bool CookieBase::IsThirdPartyPartitioned() const {
539 return IsPartitioned() && !IsFirstPartyPartitioned();
540 }
541
DomainWithoutDot() const542 std::string CookieBase::DomainWithoutDot() const {
543 return cookie_util::CookieDomainAsHost(domain_);
544 }
545
UniqueKey() const546 CookieBase::UniqueCookieKey CookieBase::UniqueKey() const {
547 std::optional<CookieSourceScheme> source_scheme =
548 cookie_util::IsSchemeBoundCookiesEnabled()
549 ? std::make_optional(source_scheme_)
550 : std::nullopt;
551 std::optional<int> source_port = cookie_util::IsPortBoundCookiesEnabled()
552 ? std::make_optional(source_port_)
553 : std::nullopt;
554
555 return std::make_tuple(partition_key_, name_, domain_, path_, source_scheme,
556 source_port);
557 }
558
UniqueDomainKey() const559 CookieBase::UniqueDomainCookieKey CookieBase::UniqueDomainKey() const {
560 std::optional<CookieSourceScheme> source_scheme =
561 cookie_util::IsSchemeBoundCookiesEnabled()
562 ? std::make_optional(source_scheme_)
563 : std::nullopt;
564
565 return std::make_tuple(partition_key_, name_, domain_, path_, source_scheme);
566 }
567
SetSourcePort(int port)568 void CookieBase::SetSourcePort(int port) {
569 source_port_ = ValidateAndAdjustSourcePort(port);
570 }
571
572 CookieBase::CookieBase() = default;
573
574 CookieBase::CookieBase(const CookieBase& other) = default;
575
576 CookieBase::CookieBase(CookieBase&& other) = default;
577
578 CookieBase& CookieBase::operator=(const CookieBase& other) = default;
579
580 CookieBase& CookieBase::operator=(CookieBase&& other) = default;
581
582 CookieBase::~CookieBase() = default;
583
CookieBase(std::string name,std::string domain,std::string path,base::Time creation,bool secure,bool httponly,CookieSameSite same_site,std::optional<CookiePartitionKey> partition_key,CookieSourceScheme source_scheme,int source_port)584 CookieBase::CookieBase(std::string name,
585 std::string domain,
586 std::string path,
587 base::Time creation,
588 bool secure,
589 bool httponly,
590 CookieSameSite same_site,
591 std::optional<CookiePartitionKey> partition_key,
592 CookieSourceScheme source_scheme,
593 int source_port)
594 : name_(std::move(name)),
595 domain_(std::move(domain)),
596 path_(std::move(path)),
597 creation_date_(creation),
598 secure_(secure),
599 httponly_(httponly),
600 same_site_(same_site),
601 partition_key_(std::move(partition_key)),
602 source_scheme_(source_scheme),
603 source_port_(source_port) {}
604
GetEffectiveSameSite(CookieAccessSemantics access_semantics) const605 CookieEffectiveSameSite CookieBase::GetEffectiveSameSite(
606 CookieAccessSemantics access_semantics) const {
607 base::TimeDelta lax_allow_unsafe_threshold_age =
608 base::FeatureList::IsEnabled(
609 features::kSameSiteDefaultChecksMethodRigorously)
610 ? base::TimeDelta::Min()
611 : (base::FeatureList::IsEnabled(
612 features::kShortLaxAllowUnsafeThreshold)
613 ? kShortLaxAllowUnsafeMaxAge
614 : kLaxAllowUnsafeMaxAge);
615
616 switch (SameSite()) {
617 // If a cookie does not have a SameSite attribute, the effective SameSite
618 // mode depends on the access semantics and whether the cookie is
619 // recently-created.
620 case CookieSameSite::UNSPECIFIED:
621 return (access_semantics == CookieAccessSemantics::LEGACY)
622 ? CookieEffectiveSameSite::NO_RESTRICTION
623 : (IsRecentlyCreated(lax_allow_unsafe_threshold_age)
624 ? CookieEffectiveSameSite::LAX_MODE_ALLOW_UNSAFE
625 : CookieEffectiveSameSite::LAX_MODE);
626 case CookieSameSite::NO_RESTRICTION:
627 return CookieEffectiveSameSite::NO_RESTRICTION;
628 case CookieSameSite::LAX_MODE:
629 return CookieEffectiveSameSite::LAX_MODE;
630 case CookieSameSite::STRICT_MODE:
631 return CookieEffectiveSameSite::STRICT_MODE;
632 }
633 }
634
IsRecentlyCreated(base::TimeDelta age_threshold) const635 bool CookieBase::IsRecentlyCreated(base::TimeDelta age_threshold) const {
636 return (base::Time::Now() - creation_date_) <= age_threshold;
637 }
638
639 // static
ValidateAndAdjustSourcePort(int port)640 int CookieBase::ValidateAndAdjustSourcePort(int port) {
641 if ((port >= 0 && port <= 65535) || port == url::PORT_UNSPECIFIED) {
642 // 0 would be really weird as it has a special meaning, but it's still
643 // technically a valid tcp/ip port so we're going to accept it here.
644 return port;
645 }
646 return url::PORT_INVALID;
647 }
648
649 } // namespace net
650