canonical_cookie.cc 67 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707
  1. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. // Portions of this code based on Mozilla:
  5. // (netwerk/cookie/src/nsCookieService.cpp)
  6. /* ***** BEGIN LICENSE BLOCK *****
  7. * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  8. *
  9. * The contents of this file are subject to the Mozilla Public License Version
  10. * 1.1 (the "License"); you may not use this file except in compliance with
  11. * the License. You may obtain a copy of the License at
  12. * http://www.mozilla.org/MPL/
  13. *
  14. * Software distributed under the License is distributed on an "AS IS" basis,
  15. * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  16. * for the specific language governing rights and limitations under the
  17. * License.
  18. *
  19. * The Original Code is mozilla.org code.
  20. *
  21. * The Initial Developer of the Original Code is
  22. * Netscape Communications Corporation.
  23. * Portions created by the Initial Developer are Copyright (C) 2003
  24. * the Initial Developer. All Rights Reserved.
  25. *
  26. * Contributor(s):
  27. * Daniel Witte (dwitte@stanford.edu)
  28. * Michiel van Leeuwen (mvl@exedo.nl)
  29. *
  30. * Alternatively, the contents of this file may be used under the terms of
  31. * either the GNU General Public License Version 2 or later (the "GPL"), or
  32. * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  33. * in which case the provisions of the GPL or the LGPL are applicable instead
  34. * of those above. If you wish to allow use of your version of this file only
  35. * under the terms of either the GPL or the LGPL, and not to allow others to
  36. * use your version of this file under the terms of the MPL, indicate your
  37. * decision by deleting the provisions above and replace them with the notice
  38. * and other provisions required by the GPL or the LGPL. If you do not delete
  39. * the provisions above, a recipient may use your version of this file under
  40. * the terms of any one of the MPL, the GPL or the LGPL.
  41. *
  42. * ***** END LICENSE BLOCK ***** */
  43. #include "net/cookies/canonical_cookie.h"
  44. #include <limits>
  45. #include <utility>
  46. #include "base/containers/contains.h"
  47. #include "base/feature_list.h"
  48. #include "base/format_macros.h"
  49. #include "base/logging.h"
  50. #include "base/memory/ptr_util.h"
  51. #include "base/metrics/histogram_functions.h"
  52. #include "base/metrics/histogram_macros.h"
  53. #include "base/strings/strcat.h"
  54. #include "base/strings/string_number_conversions.h"
  55. #include "base/strings/string_piece.h"
  56. #include "base/strings/string_util.h"
  57. #include "base/strings/stringprintf.h"
  58. #include "net/base/features.h"
  59. #include "net/base/url_util.h"
  60. #include "net/cookies/cookie_constants.h"
  61. #include "net/cookies/cookie_inclusion_status.h"
  62. #include "net/cookies/cookie_options.h"
  63. #include "net/cookies/cookie_util.h"
  64. #include "net/cookies/parsed_cookie.h"
  65. #include "third_party/abseil-cpp/absl/types/optional.h"
  66. #include "url/gurl.h"
  67. #include "url/url_canon.h"
  68. #include "url/url_util.h"
  69. using base::Time;
  70. namespace net {
  71. static constexpr int kMinutesInTwelveHours = 12 * 60;
  72. static constexpr int kMinutesInTwentyFourHours = 24 * 60;
  73. namespace {
  74. // Determine the cookie domain to use for setting the specified cookie.
  75. bool GetCookieDomain(const GURL& url,
  76. const ParsedCookie& pc,
  77. CookieInclusionStatus& status,
  78. std::string* result) {
  79. std::string domain_string;
  80. if (pc.HasDomain())
  81. domain_string = pc.Domain();
  82. return cookie_util::GetCookieDomainWithString(url, domain_string, status,
  83. result);
  84. }
  85. // Compares cookies using name, domain and path, so that "equivalent" cookies
  86. // (per RFC 2965) are equal to each other.
  87. int PartialCookieOrdering(const CanonicalCookie& a, const CanonicalCookie& b) {
  88. int diff = a.Name().compare(b.Name());
  89. if (diff != 0)
  90. return diff;
  91. diff = a.Domain().compare(b.Domain());
  92. if (diff != 0)
  93. return diff;
  94. return a.Path().compare(b.Path());
  95. }
  96. void AppendCookieLineEntry(const CanonicalCookie& cookie,
  97. std::string* cookie_line) {
  98. if (!cookie_line->empty())
  99. *cookie_line += "; ";
  100. // In Mozilla, if you set a cookie like "AAA", it will have an empty token
  101. // and a value of "AAA". When it sends the cookie back, it will send "AAA",
  102. // so we need to avoid sending "=AAA" for a blank token value.
  103. if (!cookie.Name().empty())
  104. *cookie_line += cookie.Name() + "=";
  105. *cookie_line += cookie.Value();
  106. }
  107. // Captures Strict -> Lax context downgrade with Strict cookie
  108. bool IsBreakingStrictToLaxDowngrade(
  109. CookieOptions::SameSiteCookieContext::ContextType context,
  110. CookieOptions::SameSiteCookieContext::ContextType schemeful_context,
  111. CookieEffectiveSameSite effective_same_site,
  112. bool is_cookie_being_set) {
  113. if (context ==
  114. CookieOptions::SameSiteCookieContext::ContextType::SAME_SITE_STRICT &&
  115. schemeful_context ==
  116. CookieOptions::SameSiteCookieContext::ContextType::SAME_SITE_LAX &&
  117. effective_same_site == CookieEffectiveSameSite::STRICT_MODE) {
  118. // This downgrade only applies when a SameSite=Strict cookie is being sent.
  119. // A Strict -> Lax downgrade will not affect a Strict cookie which is being
  120. // set because it will be set in either context.
  121. return !is_cookie_being_set;
  122. }
  123. return false;
  124. }
  125. // Captures Strict -> Cross-site context downgrade with {Strict, Lax} cookie
  126. // Captures Strict -> Lax Unsafe context downgrade with {Strict, Lax} cookie.
  127. // This is treated as a cross-site downgrade due to the Lax Unsafe context
  128. // behaving like cross-site.
  129. bool IsBreakingStrictToCrossDowngrade(
  130. CookieOptions::SameSiteCookieContext::ContextType context,
  131. CookieOptions::SameSiteCookieContext::ContextType schemeful_context,
  132. CookieEffectiveSameSite effective_same_site) {
  133. bool breaking_schemeful_context =
  134. schemeful_context ==
  135. CookieOptions::SameSiteCookieContext::ContextType::CROSS_SITE ||
  136. schemeful_context == CookieOptions::SameSiteCookieContext::ContextType::
  137. SAME_SITE_LAX_METHOD_UNSAFE;
  138. bool strict_lax_enforcement =
  139. effective_same_site == CookieEffectiveSameSite::STRICT_MODE ||
  140. effective_same_site == CookieEffectiveSameSite::LAX_MODE ||
  141. // Treat LAX_MODE_ALLOW_UNSAFE the same as LAX_MODE for the purposes of
  142. // our SameSite enforcement check.
  143. effective_same_site == CookieEffectiveSameSite::LAX_MODE_ALLOW_UNSAFE;
  144. if (context ==
  145. CookieOptions::SameSiteCookieContext::ContextType::SAME_SITE_STRICT &&
  146. breaking_schemeful_context && strict_lax_enforcement) {
  147. return true;
  148. }
  149. return false;
  150. }
  151. // Captures Lax -> Cross context downgrade with {Strict, Lax} cookies.
  152. // Ignores Lax Unsafe context.
  153. bool IsBreakingLaxToCrossDowngrade(
  154. CookieOptions::SameSiteCookieContext::ContextType context,
  155. CookieOptions::SameSiteCookieContext::ContextType schemeful_context,
  156. CookieEffectiveSameSite effective_same_site,
  157. bool is_cookie_being_set) {
  158. bool lax_enforcement =
  159. effective_same_site == CookieEffectiveSameSite::LAX_MODE ||
  160. // Treat LAX_MODE_ALLOW_UNSAFE the same as LAX_MODE for the purposes of
  161. // our SameSite enforcement check.
  162. effective_same_site == CookieEffectiveSameSite::LAX_MODE_ALLOW_UNSAFE;
  163. if (context ==
  164. CookieOptions::SameSiteCookieContext::ContextType::SAME_SITE_LAX &&
  165. schemeful_context ==
  166. CookieOptions::SameSiteCookieContext::ContextType::CROSS_SITE) {
  167. // For SameSite=Strict cookies this downgrade only applies when it is being
  168. // set. A Lax -> Cross downgrade will not affect a Strict cookie which is
  169. // being sent because it wouldn't be sent in either context.
  170. return effective_same_site == CookieEffectiveSameSite::STRICT_MODE
  171. ? is_cookie_being_set
  172. : lax_enforcement;
  173. }
  174. return false;
  175. }
  176. void ApplySameSiteCookieWarningToStatus(
  177. CookieSameSite samesite,
  178. CookieEffectiveSameSite effective_samesite,
  179. bool is_secure,
  180. const CookieOptions::SameSiteCookieContext& same_site_context,
  181. CookieInclusionStatus* status,
  182. bool is_cookie_being_set) {
  183. if (samesite == CookieSameSite::UNSPECIFIED &&
  184. same_site_context.GetContextForCookieInclusion() <
  185. CookieOptions::SameSiteCookieContext::ContextType::SAME_SITE_LAX) {
  186. status->AddWarningReason(
  187. CookieInclusionStatus::WARN_SAMESITE_UNSPECIFIED_CROSS_SITE_CONTEXT);
  188. }
  189. if (effective_samesite == CookieEffectiveSameSite::LAX_MODE_ALLOW_UNSAFE &&
  190. same_site_context.GetContextForCookieInclusion() ==
  191. CookieOptions::SameSiteCookieContext::ContextType::
  192. SAME_SITE_LAX_METHOD_UNSAFE) {
  193. // This warning is more specific so remove the previous, more general,
  194. // warning.
  195. status->RemoveWarningReason(
  196. CookieInclusionStatus::WARN_SAMESITE_UNSPECIFIED_CROSS_SITE_CONTEXT);
  197. status->AddWarningReason(
  198. CookieInclusionStatus::WARN_SAMESITE_UNSPECIFIED_LAX_ALLOW_UNSAFE);
  199. }
  200. if (samesite == CookieSameSite::NO_RESTRICTION && !is_secure) {
  201. status->AddWarningReason(
  202. CookieInclusionStatus::WARN_SAMESITE_NONE_INSECURE);
  203. }
  204. // Add a warning if the cookie would be accessible in
  205. // |same_site_context|::context but not in
  206. // |same_site_context|::schemeful_context.
  207. if (IsBreakingStrictToLaxDowngrade(same_site_context.context(),
  208. same_site_context.schemeful_context(),
  209. effective_samesite, is_cookie_being_set)) {
  210. status->AddWarningReason(
  211. CookieInclusionStatus::WARN_STRICT_LAX_DOWNGRADE_STRICT_SAMESITE);
  212. } else if (IsBreakingStrictToCrossDowngrade(
  213. same_site_context.context(),
  214. same_site_context.schemeful_context(), effective_samesite)) {
  215. // Which warning to apply depends on the SameSite value.
  216. if (effective_samesite == CookieEffectiveSameSite::STRICT_MODE) {
  217. status->AddWarningReason(
  218. CookieInclusionStatus::WARN_STRICT_CROSS_DOWNGRADE_STRICT_SAMESITE);
  219. } else {
  220. // LAX_MODE or LAX_MODE_ALLOW_UNSAFE.
  221. status->AddWarningReason(
  222. CookieInclusionStatus::WARN_STRICT_CROSS_DOWNGRADE_LAX_SAMESITE);
  223. }
  224. } else if (IsBreakingLaxToCrossDowngrade(
  225. same_site_context.context(),
  226. same_site_context.schemeful_context(), effective_samesite,
  227. is_cookie_being_set)) {
  228. // Which warning to apply depends on the SameSite value.
  229. if (effective_samesite == CookieEffectiveSameSite::STRICT_MODE) {
  230. status->AddWarningReason(
  231. CookieInclusionStatus::WARN_LAX_CROSS_DOWNGRADE_STRICT_SAMESITE);
  232. } else {
  233. // LAX_MODE or LAX_MODE_ALLOW_UNSAFE.
  234. // This warning applies to both set/send.
  235. status->AddWarningReason(
  236. CookieInclusionStatus::WARN_LAX_CROSS_DOWNGRADE_LAX_SAMESITE);
  237. }
  238. }
  239. // Apply warning for whether inclusion was changed by considering redirects
  240. // for the SameSite context calculation. This does not look at the actual
  241. // inclusion or exclusion, but only at whether the inclusion differs between
  242. // considering redirects and not.
  243. using ContextDowngradeType = CookieOptions::SameSiteCookieContext::
  244. ContextMetadata::ContextDowngradeType;
  245. const auto& metadata = same_site_context.GetMetadataForCurrentSchemefulMode();
  246. bool apply_cross_site_redirect_downgrade_warning = false;
  247. switch (effective_samesite) {
  248. case CookieEffectiveSameSite::STRICT_MODE:
  249. // Strict contexts are all normalized to lax for cookie writes, so a
  250. // strict-to-{lax,cross} downgrade cannot occur for response cookies.
  251. apply_cross_site_redirect_downgrade_warning =
  252. is_cookie_being_set ? metadata.cross_site_redirect_downgrade ==
  253. ContextDowngradeType::kLaxToCross
  254. : (metadata.cross_site_redirect_downgrade ==
  255. ContextDowngradeType::kStrictToLax ||
  256. metadata.cross_site_redirect_downgrade ==
  257. ContextDowngradeType::kStrictToCross);
  258. break;
  259. case CookieEffectiveSameSite::LAX_MODE:
  260. case CookieEffectiveSameSite::LAX_MODE_ALLOW_UNSAFE:
  261. // Note that a lax-to-cross downgrade can only happen for response
  262. // cookies, because a laxly same-site context only happens for a safe
  263. // top-level cross-site request, which cannot be downgraded due to a
  264. // cross-site redirect to a non-top-level or unsafe cross-site request.
  265. apply_cross_site_redirect_downgrade_warning =
  266. metadata.cross_site_redirect_downgrade ==
  267. (is_cookie_being_set ? ContextDowngradeType::kLaxToCross
  268. : ContextDowngradeType::kStrictToCross);
  269. break;
  270. default:
  271. break;
  272. }
  273. if (apply_cross_site_redirect_downgrade_warning) {
  274. status->AddWarningReason(
  275. CookieInclusionStatus::
  276. WARN_CROSS_SITE_REDIRECT_DOWNGRADE_CHANGES_INCLUSION);
  277. }
  278. // If there are reasons to exclude the cookie other than SameSite, don't warn
  279. // about the cookie at all.
  280. status->MaybeClearSameSiteWarning();
  281. }
  282. // Converts CookieSameSite to CookieSameSiteForMetrics by adding 1 to it.
  283. CookieSameSiteForMetrics CookieSameSiteToCookieSameSiteForMetrics(
  284. CookieSameSite enum_in) {
  285. return static_cast<CookieSameSiteForMetrics>((static_cast<int>(enum_in) + 1));
  286. }
  287. // Checks if `port` is within [0,65535] or url::PORT_UNSPECIFIED. Returns `port`
  288. // if so and url::PORT_INVALID otherwise.
  289. int ValidateAndAdjustSourcePort(int port) {
  290. if ((port >= 0 && port <= 65535) || port == url::PORT_UNSPECIFIED) {
  291. // 0 would be really weird as it has a special meaning, but it's still
  292. // technically a valid tcp/ip port so we're going to accept it here.
  293. return port;
  294. }
  295. return url::PORT_INVALID;
  296. }
  297. // Tests that a cookie has the attributes for a valid __Host- prefix without
  298. // testing that the prefix is in the cookie name.
  299. bool HasValidHostPrefixAttributes(const GURL& url,
  300. bool secure,
  301. const std::string& domain,
  302. const std::string& path) {
  303. if (!secure || !url.SchemeIsCryptographic() || path != "/")
  304. return false;
  305. return domain.empty() || (url.HostIsIPAddress() && url.host() == domain);
  306. }
  307. } // namespace
  308. CookieAccessParams::CookieAccessParams(CookieAccessSemantics access_semantics,
  309. bool delegate_treats_url_as_trustworthy,
  310. CookieSamePartyStatus same_party_status)
  311. : access_semantics(access_semantics),
  312. delegate_treats_url_as_trustworthy(delegate_treats_url_as_trustworthy),
  313. same_party_status(same_party_status) {}
  314. CanonicalCookie::CanonicalCookie() = default;
  315. CanonicalCookie::CanonicalCookie(const CanonicalCookie& other) = default;
  316. CanonicalCookie::CanonicalCookie(CanonicalCookie&& other) = default;
  317. CanonicalCookie& CanonicalCookie::operator=(const CanonicalCookie& other) =
  318. default;
  319. CanonicalCookie& CanonicalCookie::operator=(CanonicalCookie&& other) = default;
  320. CanonicalCookie::CanonicalCookie(
  321. std::string name,
  322. std::string value,
  323. std::string domain,
  324. std::string path,
  325. base::Time creation,
  326. base::Time expiration,
  327. base::Time last_access,
  328. base::Time last_update,
  329. bool secure,
  330. bool httponly,
  331. CookieSameSite same_site,
  332. CookiePriority priority,
  333. bool same_party,
  334. absl::optional<CookiePartitionKey> partition_key,
  335. CookieSourceScheme source_scheme,
  336. int source_port)
  337. : name_(std::move(name)),
  338. value_(std::move(value)),
  339. domain_(std::move(domain)),
  340. path_(std::move(path)),
  341. creation_date_(creation),
  342. expiry_date_(expiration),
  343. last_access_date_(last_access),
  344. last_update_date_(last_update),
  345. secure_(secure),
  346. httponly_(httponly),
  347. same_site_(same_site),
  348. priority_(priority),
  349. same_party_(same_party),
  350. partition_key_(std::move(partition_key)),
  351. source_scheme_(source_scheme),
  352. source_port_(source_port) {}
  353. CanonicalCookie::~CanonicalCookie() = default;
  354. // static
  355. std::string CanonicalCookie::CanonPathWithString(
  356. const GURL& url,
  357. const std::string& path_string) {
  358. // The path was supplied in the cookie, we'll take it.
  359. if (!path_string.empty() && path_string[0] == '/')
  360. return path_string;
  361. // The path was not supplied in the cookie or invalid, we will default
  362. // to the current URL path.
  363. // """Defaults to the path of the request URL that generated the
  364. // Set-Cookie response, up to, but not including, the
  365. // right-most /."""
  366. // How would this work for a cookie on /? We will include it then.
  367. const std::string& url_path = url.path();
  368. size_t idx = url_path.find_last_of('/');
  369. // The cookie path was invalid or a single '/'.
  370. if (idx == 0 || idx == std::string::npos)
  371. return std::string("/");
  372. // Return up to the rightmost '/'.
  373. return url_path.substr(0, idx);
  374. }
  375. // static
  376. Time CanonicalCookie::ParseExpiration(const ParsedCookie& pc,
  377. const Time& current,
  378. const Time& server_time) {
  379. // First, try the Max-Age attribute.
  380. if (pc.HasMaxAge()) {
  381. int64_t max_age = 0;
  382. // Use the output if StringToInt64 returns true ("perfect" conversion). This
  383. // case excludes overflow/underflow, leading/trailing whitespace, non-number
  384. // strings, and empty string. (ParsedCookie trims whitespace.)
  385. if (base::StringToInt64(pc.MaxAge(), &max_age)) {
  386. // RFC 6265bis algorithm for parsing Max-Age:
  387. // "If delta-seconds is less than or equal to zero (0), let expiry-
  388. // time be the earliest representable date and time. ... "
  389. if (max_age <= 0)
  390. return Time::Min();
  391. // "... Otherwise, let the expiry-time be the current date and time plus
  392. // delta-seconds seconds."
  393. return current + base::Seconds(max_age);
  394. } else {
  395. // If the conversion wasn't perfect, but the best-effort conversion
  396. // resulted in an overflow/underflow, use the min/max representable time.
  397. // (This is alluded to in the spec, which says the user agent MAY clip an
  398. // Expires attribute to a saturated time. We'll do the same for Max-Age.)
  399. if (max_age == std::numeric_limits<int64_t>::min())
  400. return Time::Min();
  401. if (max_age == std::numeric_limits<int64_t>::max())
  402. return Time::Max();
  403. }
  404. }
  405. // Try the Expires attribute.
  406. if (pc.HasExpires() && !pc.Expires().empty()) {
  407. // Adjust for clock skew between server and host.
  408. Time parsed_expiry = cookie_util::ParseCookieExpirationTime(pc.Expires());
  409. if (!parsed_expiry.is_null()) {
  410. // Record metrics related to prevalence of clock skew.
  411. base::TimeDelta clock_skew = (current - server_time);
  412. // Record the magnitude (absolute value) of the skew in minutes.
  413. int clock_skew_magnitude = clock_skew.magnitude().InMinutes();
  414. // Determine the new expiry with clock skew factored in.
  415. Time adjusted_expiry = parsed_expiry + (current - server_time);
  416. if (clock_skew.is_positive() || clock_skew.is_zero()) {
  417. UMA_HISTOGRAM_CUSTOM_COUNTS("Cookie.ClockSkew.AddMinutes",
  418. clock_skew_magnitude, 1,
  419. kMinutesInTwelveHours, 100);
  420. UMA_HISTOGRAM_CUSTOM_COUNTS("Cookie.ClockSkew.AddMinutes12To24Hours",
  421. clock_skew_magnitude, kMinutesInTwelveHours,
  422. kMinutesInTwentyFourHours, 100);
  423. // Also record the range of minutes added that allowed the cookie to
  424. // avoid expiring immediately.
  425. if (parsed_expiry <= Time::Now() && adjusted_expiry > Time::Now()) {
  426. UMA_HISTOGRAM_CUSTOM_COUNTS(
  427. "Cookie.ClockSkew.WithoutAddMinutesExpires", clock_skew_magnitude,
  428. 1, kMinutesInTwentyFourHours, 100);
  429. }
  430. } else if (clock_skew.is_negative()) {
  431. // These histograms only support positive numbers, so negative skews
  432. // will be converted to positive (via magnitude) before recording.
  433. UMA_HISTOGRAM_CUSTOM_COUNTS("Cookie.ClockSkew.SubtractMinutes",
  434. clock_skew_magnitude, 1,
  435. kMinutesInTwelveHours, 100);
  436. UMA_HISTOGRAM_CUSTOM_COUNTS(
  437. "Cookie.ClockSkew.SubtractMinutes12To24Hours", clock_skew_magnitude,
  438. kMinutesInTwelveHours, kMinutesInTwentyFourHours, 100);
  439. }
  440. // Record if we were going to expire the cookie before we added the clock
  441. // skew.
  442. UMA_HISTOGRAM_BOOLEAN(
  443. "Cookie.ClockSkew.ExpiredWithoutSkew",
  444. parsed_expiry <= Time::Now() && adjusted_expiry > Time::Now());
  445. return adjusted_expiry;
  446. }
  447. }
  448. // Invalid or no expiration, session cookie.
  449. return Time();
  450. }
  451. // static
  452. base::Time CanonicalCookie::ValidateAndAdjustExpiryDate(
  453. const base::Time& expiry_date,
  454. const base::Time& creation_date) {
  455. if (expiry_date.is_null())
  456. return expiry_date;
  457. base::Time fixed_creation_date = creation_date;
  458. if (fixed_creation_date.is_null()) {
  459. // TODO(crbug.com/1264458): Push this logic into
  460. // CanonicalCookie::CreateSanitizedCookie. The four sites that call it
  461. // with a null `creation_date` (CanonicalCookie::Create cannot be called
  462. // this way) are:
  463. // * GaiaCookieManagerService::ForceOnCookieChangeProcessing
  464. // * CookiesSetFunction::Run
  465. // * cookie_store.cc::ToCanonicalCookie
  466. // * network_handler.cc::MakeCookieFromProtocolValues
  467. fixed_creation_date = base::Time::Now();
  468. }
  469. if (base::FeatureList::IsEnabled(features::kClampCookieExpiryTo400Days)) {
  470. base::Time maximum_expiry_date = fixed_creation_date + base::Days(400);
  471. if (expiry_date > maximum_expiry_date)
  472. return maximum_expiry_date;
  473. }
  474. return expiry_date;
  475. }
  476. // static
  477. std::unique_ptr<CanonicalCookie> CanonicalCookie::Create(
  478. const GURL& url,
  479. const std::string& cookie_line,
  480. const base::Time& creation_time,
  481. absl::optional<base::Time> server_time,
  482. absl::optional<CookiePartitionKey> cookie_partition_key,
  483. CookieInclusionStatus* status) {
  484. // Put a pointer on the stack so the rest of the function can assign to it if
  485. // the default nullptr is passed in.
  486. CookieInclusionStatus blank_status;
  487. if (status == nullptr) {
  488. status = &blank_status;
  489. }
  490. *status = CookieInclusionStatus();
  491. ParsedCookie parsed_cookie(cookie_line, status);
  492. if (!parsed_cookie.IsValid()) {
  493. DVLOG(net::cookie_util::kVlogSetCookies)
  494. << "WARNING: Couldn't parse cookie";
  495. DCHECK(!status->IsInclude());
  496. // Don't continue, because an invalid ParsedCookie doesn't have any
  497. // attributes.
  498. // TODO(chlily): Log metrics.
  499. return nullptr;
  500. }
  501. // Record warning for non-ASCII octecs in the Domain attribute.
  502. // This should lead to rejection of the cookie in the future.
  503. UMA_HISTOGRAM_BOOLEAN("Cookie.DomainHasNonASCII",
  504. parsed_cookie.HasDomain() &&
  505. !base::IsStringASCII(parsed_cookie.Domain()));
  506. std::string cookie_domain;
  507. if (!GetCookieDomain(url, parsed_cookie, *status, &cookie_domain)) {
  508. DVLOG(net::cookie_util::kVlogSetCookies)
  509. << "Create() failed to get a valid cookie domain";
  510. status->AddExclusionReason(CookieInclusionStatus::EXCLUDE_INVALID_DOMAIN);
  511. }
  512. std::string cookie_path = CanonPathWithString(
  513. url, parsed_cookie.HasPath() ? parsed_cookie.Path() : std::string());
  514. Time cookie_server_time(creation_time);
  515. if (server_time.has_value() && !server_time->is_null())
  516. cookie_server_time = server_time.value();
  517. DCHECK(!creation_time.is_null());
  518. Time cookie_expires = CanonicalCookie::ParseExpiration(
  519. parsed_cookie, creation_time, cookie_server_time);
  520. cookie_expires = ValidateAndAdjustExpiryDate(cookie_expires, creation_time);
  521. CookiePrefix prefix = GetCookiePrefix(parsed_cookie.Name());
  522. bool is_cookie_prefix_valid = IsCookiePrefixValid(prefix, url, parsed_cookie);
  523. RecordCookiePrefixMetrics(prefix, is_cookie_prefix_valid);
  524. if (parsed_cookie.Name() == "") {
  525. is_cookie_prefix_valid = !HasHiddenPrefixName(parsed_cookie.Value());
  526. }
  527. if (!is_cookie_prefix_valid) {
  528. DVLOG(net::cookie_util::kVlogSetCookies)
  529. << "Create() failed because the cookie violated prefix rules.";
  530. status->AddExclusionReason(CookieInclusionStatus::EXCLUDE_INVALID_PREFIX);
  531. }
  532. bool is_same_party_valid = IsCookieSamePartyValid(parsed_cookie);
  533. if (!is_same_party_valid) {
  534. status->AddExclusionReason(
  535. CookieInclusionStatus::EXCLUDE_INVALID_SAMEPARTY);
  536. }
  537. // Collect metrics on whether usage of SameParty attribute is correct.
  538. if (parsed_cookie.IsSameParty())
  539. UMA_HISTOGRAM_BOOLEAN("Cookie.IsSamePartyValid", is_same_party_valid);
  540. bool partition_has_nonce = CookiePartitionKey::HasNonce(cookie_partition_key);
  541. bool is_partitioned_valid =
  542. IsCookiePartitionedValid(url, parsed_cookie, partition_has_nonce);
  543. if (!is_partitioned_valid) {
  544. status->AddExclusionReason(
  545. CookieInclusionStatus::EXCLUDE_INVALID_PARTITIONED);
  546. }
  547. // Collect metrics on whether usage of the Partitioned attribute is correct.
  548. // Do not include implicit nonce-based partitioned cookies in these metrics.
  549. if (parsed_cookie.IsPartitioned()) {
  550. if (!partition_has_nonce)
  551. UMA_HISTOGRAM_BOOLEAN("Cookie.IsPartitionedValid", is_partitioned_valid);
  552. } else if (!partition_has_nonce) {
  553. cookie_partition_key = absl::nullopt;
  554. }
  555. if (!status->IsInclude())
  556. return nullptr;
  557. CookieSameSiteString samesite_string = CookieSameSiteString::kUnspecified;
  558. CookieSameSite samesite = parsed_cookie.SameSite(&samesite_string);
  559. CookieSourceScheme source_scheme = url.SchemeIsCryptographic()
  560. ? CookieSourceScheme::kSecure
  561. : CookieSourceScheme::kNonSecure;
  562. // Get the port, this will get a default value if a port isn't provided.
  563. int source_port = ValidateAndAdjustSourcePort(url.EffectiveIntPort());
  564. std::unique_ptr<CanonicalCookie> cc = base::WrapUnique(new CanonicalCookie(
  565. parsed_cookie.Name(), parsed_cookie.Value(), cookie_domain, cookie_path,
  566. creation_time, cookie_expires, creation_time,
  567. /*last_update=*/base::Time::Now(), parsed_cookie.IsSecure(),
  568. parsed_cookie.IsHttpOnly(), samesite, parsed_cookie.Priority(),
  569. parsed_cookie.IsSameParty(), cookie_partition_key, source_scheme,
  570. source_port));
  571. // TODO(chlily): Log metrics.
  572. if (!cc->IsCanonical()) {
  573. status->AddExclusionReason(
  574. net::CookieInclusionStatus::EXCLUDE_FAILURE_TO_STORE);
  575. return nullptr;
  576. }
  577. RecordCookieSameSiteAttributeValueHistogram(samesite_string,
  578. parsed_cookie.IsSameParty());
  579. UMA_HISTOGRAM_BOOLEAN("Cookie.ControlCharacterTruncation",
  580. parsed_cookie.HasTruncatedNameOrValue());
  581. UMA_HISTOGRAM_ENUMERATION(
  582. "Cookie.TruncatingCharacterInCookieString",
  583. parsed_cookie.GetTruncatingCharacterInCookieStringType());
  584. return cc;
  585. }
  586. // static
  587. std::unique_ptr<CanonicalCookie> CanonicalCookie::CreateSanitizedCookie(
  588. const GURL& url,
  589. const std::string& name,
  590. const std::string& value,
  591. const std::string& domain,
  592. const std::string& path,
  593. base::Time creation_time,
  594. base::Time expiration_time,
  595. base::Time last_access_time,
  596. bool secure,
  597. bool http_only,
  598. CookieSameSite same_site,
  599. CookiePriority priority,
  600. bool same_party,
  601. absl::optional<CookiePartitionKey> partition_key,
  602. CookieInclusionStatus* status) {
  603. // Put a pointer on the stack so the rest of the function can assign to it if
  604. // the default nullptr is passed in.
  605. CookieInclusionStatus blank_status;
  606. if (status == nullptr) {
  607. status = &blank_status;
  608. }
  609. *status = CookieInclusionStatus();
  610. // Validate consistency of passed arguments.
  611. if (ParsedCookie::ParseTokenString(name) != name) {
  612. status->AddExclusionReason(
  613. net::CookieInclusionStatus::EXCLUDE_FAILURE_TO_STORE);
  614. } else if (ParsedCookie::ParseValueString(value) != value) {
  615. status->AddExclusionReason(
  616. net::CookieInclusionStatus::EXCLUDE_FAILURE_TO_STORE);
  617. } else if (ParsedCookie::ParseValueString(path) != path) {
  618. // NOTE: If `path` contains "terminating characters" ('\r', '\n', and
  619. // '\0'), ';', or leading / trailing whitespace, path will be rejected,
  620. // but any other control characters will just get URL-encoded below.
  621. status->AddExclusionReason(
  622. net::CookieInclusionStatus::EXCLUDE_FAILURE_TO_STORE);
  623. }
  624. if (base::FeatureList::IsEnabled(features::kExtraCookieValidityChecks)) {
  625. // Validate name and value against character set and size limit constraints.
  626. // If IsValidCookieNameValuePair identifies that `name` and/or `value` are
  627. // invalid, it will add an ExclusionReason to `status`.
  628. ParsedCookie::IsValidCookieNameValuePair(name, value, status);
  629. } else if (!ParsedCookie::IsValidCookieAttributeValueLegacy(name) ||
  630. !ParsedCookie::IsValidCookieAttributeValueLegacy(value) ||
  631. (name.empty() && value.empty())) {
  632. status->AddExclusionReason(
  633. net::CookieInclusionStatus::EXCLUDE_FAILURE_TO_STORE);
  634. }
  635. // Validate domain against character set and size limit constraints.
  636. bool domain_is_valid = true;
  637. if ((ParsedCookie::ParseValueString(domain) != domain)) {
  638. status->AddExclusionReason(
  639. net::CookieInclusionStatus::EXCLUDE_INVALID_DOMAIN);
  640. domain_is_valid = false;
  641. }
  642. if (base::FeatureList::IsEnabled(features::kExtraCookieValidityChecks)) {
  643. if (!ParsedCookie::CookieAttributeValueHasValidCharSet(domain)) {
  644. status->AddExclusionReason(
  645. net::CookieInclusionStatus::EXCLUDE_INVALID_DOMAIN);
  646. domain_is_valid = false;
  647. }
  648. if (!ParsedCookie::CookieAttributeValueHasValidSize(domain)) {
  649. status->AddExclusionReason(
  650. net::CookieInclusionStatus::EXCLUDE_ATTRIBUTE_VALUE_EXCEEDS_MAX_SIZE);
  651. domain_is_valid = false;
  652. }
  653. }
  654. const std::string& domain_attribute =
  655. domain_is_valid ? domain : std::string();
  656. std::string cookie_domain;
  657. // This validation step must happen before GetCookieDomainWithString, so it
  658. // doesn't fail DCHECKs.
  659. if (!cookie_util::DomainIsHostOnly(url.host())) {
  660. status->AddExclusionReason(
  661. net::CookieInclusionStatus::EXCLUDE_INVALID_DOMAIN);
  662. } else if (!cookie_util::GetCookieDomainWithString(url, domain_attribute,
  663. *status, &cookie_domain)) {
  664. status->AddExclusionReason(
  665. net::CookieInclusionStatus::EXCLUDE_INVALID_DOMAIN);
  666. }
  667. CookieSourceScheme source_scheme = CookieSourceScheme::kNonSecure;
  668. // This validation step must happen before SchemeIsCryptographic, so it
  669. // doesn't fail DCHECKs.
  670. if (!url.is_valid()) {
  671. status->AddExclusionReason(
  672. net::CookieInclusionStatus::EXCLUDE_INVALID_DOMAIN);
  673. } else {
  674. source_scheme = url.SchemeIsCryptographic()
  675. ? CookieSourceScheme::kSecure
  676. : CookieSourceScheme::kNonSecure;
  677. }
  678. // Get the port, this will get a default value if a port isn't provided.
  679. int source_port = ValidateAndAdjustSourcePort(url.EffectiveIntPort());
  680. std::string cookie_path = CanonicalCookie::CanonPathWithString(url, path);
  681. // Canonicalize path again to make sure it escapes characters as needed.
  682. url::Component path_component(0, cookie_path.length());
  683. url::RawCanonOutputT<char> canon_path;
  684. url::Component canon_path_component;
  685. url::CanonicalizePath(cookie_path.data(), path_component, &canon_path,
  686. &canon_path_component);
  687. std::string encoded_cookie_path = std::string(
  688. canon_path.data() + canon_path_component.begin, canon_path_component.len);
  689. if (!path.empty()) {
  690. if (cookie_path != path) {
  691. // The path attribute was specified and found to be invalid, so record an
  692. // error.
  693. status->AddExclusionReason(
  694. net::CookieInclusionStatus::EXCLUDE_FAILURE_TO_STORE);
  695. } else if (base::FeatureList::IsEnabled(
  696. features::kExtraCookieValidityChecks) &&
  697. !ParsedCookie::CookieAttributeValueHasValidSize(
  698. encoded_cookie_path)) {
  699. // The path attribute was specified and encodes into a value that's longer
  700. // than the length limit, so record an error.
  701. status->AddExclusionReason(
  702. net::CookieInclusionStatus::EXCLUDE_ATTRIBUTE_VALUE_EXCEEDS_MAX_SIZE);
  703. }
  704. }
  705. CookiePrefix prefix = GetCookiePrefix(name);
  706. if (!IsCookiePrefixValid(prefix, url, secure, domain_attribute,
  707. cookie_path)) {
  708. status->AddExclusionReason(
  709. net::CookieInclusionStatus::EXCLUDE_INVALID_PREFIX);
  710. }
  711. if (name == "" && HasHiddenPrefixName(value)) {
  712. status->AddExclusionReason(
  713. net::CookieInclusionStatus::EXCLUDE_INVALID_PREFIX);
  714. }
  715. if (!IsCookieSamePartyValid(same_party, secure, same_site)) {
  716. status->AddExclusionReason(
  717. net::CookieInclusionStatus::EXCLUDE_INVALID_SAMEPARTY);
  718. }
  719. if (!IsCookiePartitionedValid(url, secure,
  720. /*is_partitioned=*/partition_key.has_value(),
  721. /*partition_has_nonce=*/
  722. CookiePartitionKey::HasNonce(partition_key))) {
  723. status->AddExclusionReason(
  724. net::CookieInclusionStatus::EXCLUDE_INVALID_PARTITIONED);
  725. }
  726. if (!last_access_time.is_null() && creation_time.is_null()) {
  727. status->AddExclusionReason(
  728. net::CookieInclusionStatus::EXCLUDE_FAILURE_TO_STORE);
  729. }
  730. expiration_time = ValidateAndAdjustExpiryDate(expiration_time, creation_time);
  731. if (!status->IsInclude())
  732. return nullptr;
  733. std::unique_ptr<CanonicalCookie> cc = base::WrapUnique(new CanonicalCookie(
  734. name, value, cookie_domain, encoded_cookie_path, creation_time,
  735. expiration_time, last_access_time, /*last_update=*/base::Time::Now(),
  736. secure, http_only, same_site, priority, same_party, partition_key,
  737. source_scheme, source_port));
  738. DCHECK(cc->IsCanonical());
  739. return cc;
  740. }
  741. // static
  742. std::unique_ptr<CanonicalCookie> CanonicalCookie::FromStorage(
  743. std::string name,
  744. std::string value,
  745. std::string domain,
  746. std::string path,
  747. base::Time creation,
  748. base::Time expiration,
  749. base::Time last_access,
  750. base::Time last_update,
  751. bool secure,
  752. bool httponly,
  753. CookieSameSite same_site,
  754. CookiePriority priority,
  755. bool same_party,
  756. absl::optional<CookiePartitionKey> partition_key,
  757. CookieSourceScheme source_scheme,
  758. int source_port) {
  759. // We check source_port here because it could have concievably been
  760. // corrupted and changed to out of range. Eventually this would be caught by
  761. // IsCanonical*() but since the source_port is only used by metrics so far
  762. // nothing else checks it. So let's normalize it here and then update this
  763. // method when origin-bound cookies is implemented.
  764. // TODO(crbug.com/1170548)
  765. int validated_port = ValidateAndAdjustSourcePort(source_port);
  766. std::unique_ptr<CanonicalCookie> cc = base::WrapUnique(new CanonicalCookie(
  767. std::move(name), std::move(value), std::move(domain), std::move(path),
  768. creation, expiration, last_access, last_update, secure, httponly,
  769. same_site, priority, same_party, partition_key, source_scheme,
  770. validated_port));
  771. if (cc->IsCanonicalForFromStorage()) {
  772. // This will help capture the number of times a cookie is canonical but does
  773. // not have a valid name+value size length
  774. bool valid_cookie_name_value_pair =
  775. ParsedCookie::IsValidCookieNameValuePair(cc->Name(), cc->Value());
  776. UMA_HISTOGRAM_BOOLEAN("Cookie.FromStorageWithValidLength",
  777. valid_cookie_name_value_pair);
  778. } else {
  779. return nullptr;
  780. }
  781. return cc;
  782. }
  783. // static
  784. std::unique_ptr<CanonicalCookie> CanonicalCookie::CreateUnsafeCookieForTesting(
  785. const std::string& name,
  786. const std::string& value,
  787. const std::string& domain,
  788. const std::string& path,
  789. const base::Time& creation,
  790. const base::Time& expiration,
  791. const base::Time& last_access,
  792. const base::Time& last_update,
  793. bool secure,
  794. bool httponly,
  795. CookieSameSite same_site,
  796. CookiePriority priority,
  797. bool same_party,
  798. absl::optional<CookiePartitionKey> partition_key,
  799. CookieSourceScheme source_scheme,
  800. int source_port) {
  801. return base::WrapUnique(new CanonicalCookie(
  802. name, value, domain, path, creation, expiration, last_access, last_update,
  803. secure, httponly, same_site, priority, same_party, partition_key,
  804. source_scheme, source_port));
  805. }
  806. std::string CanonicalCookie::DomainWithoutDot() const {
  807. return cookie_util::CookieDomainAsHost(domain_);
  808. }
  809. void CanonicalCookie::SetSourcePort(int port) {
  810. source_port_ = ValidateAndAdjustSourcePort(port);
  811. }
  812. bool CanonicalCookie::IsEquivalentForSecureCookieMatching(
  813. const CanonicalCookie& secure_cookie) const {
  814. // Partition keys must both be equivalent.
  815. bool same_partition_key = PartitionKey() == secure_cookie.PartitionKey();
  816. // Names must be the same
  817. bool same_name = name_ == secure_cookie.Name();
  818. // They should domain-match in one direction or the other. (See RFC 6265bis
  819. // section 5.1.3.)
  820. // TODO(chlily): This does not check for the IP address case. This is bad due
  821. // to https://crbug.com/1069935.
  822. bool domain_match =
  823. IsSubdomainOf(DomainWithoutDot(), secure_cookie.DomainWithoutDot()) ||
  824. IsSubdomainOf(secure_cookie.DomainWithoutDot(), DomainWithoutDot());
  825. bool path_match = secure_cookie.IsOnPath(Path());
  826. bool equivalent_for_secure_cookie_matching =
  827. same_partition_key && same_name && domain_match && path_match;
  828. // IsEquivalent() is a stricter check than this.
  829. DCHECK(!IsEquivalent(secure_cookie) || equivalent_for_secure_cookie_matching);
  830. return equivalent_for_secure_cookie_matching;
  831. }
  832. bool CanonicalCookie::IsOnPath(const std::string& url_path) const {
  833. // A zero length would be unsafe for our trailing '/' checks, and
  834. // would also make no sense for our prefix match. The code that
  835. // creates a CanonicalCookie should make sure the path is never zero length,
  836. // but we double check anyway.
  837. if (path_.empty())
  838. return false;
  839. // The Mozilla code broke this into three cases, based on if the cookie path
  840. // was longer, the same length, or shorter than the length of the url path.
  841. // I think the approach below is simpler.
  842. // Make sure the cookie path is a prefix of the url path. If the url path is
  843. // shorter than the cookie path, then the cookie path can't be a prefix.
  844. if (!base::StartsWith(url_path, path_, base::CompareCase::SENSITIVE))
  845. return false;
  846. // |url_path| is >= |path_|, and |path_| is a prefix of |url_path|. If they
  847. // are the are the same length then they are identical, otherwise need an
  848. // additional check:
  849. // In order to avoid in correctly matching a cookie path of /blah
  850. // with a request path of '/blahblah/', we need to make sure that either
  851. // the cookie path ends in a trailing '/', or that we prefix up to a '/'
  852. // in the url path. Since we know that the url path length is greater
  853. // than the cookie path length, it's safe to index one byte past.
  854. if (path_.length() != url_path.length() && path_.back() != '/' &&
  855. url_path[path_.length()] != '/') {
  856. return false;
  857. }
  858. return true;
  859. }
  860. bool CanonicalCookie::IsDomainMatch(const std::string& host) const {
  861. return cookie_util::IsDomainMatch(domain_, host);
  862. }
  863. CookieAccessResult CanonicalCookie::IncludeForRequestURL(
  864. const GURL& url,
  865. const CookieOptions& options,
  866. const CookieAccessParams& params) const {
  867. CookieInclusionStatus status;
  868. // Filter out HttpOnly cookies, per options.
  869. if (options.exclude_httponly() && IsHttpOnly())
  870. status.AddExclusionReason(CookieInclusionStatus::EXCLUDE_HTTP_ONLY);
  871. // Secure cookies should not be included in requests for URLs with an
  872. // insecure scheme, unless it is a localhost url, or the CookieAccessDelegate
  873. // otherwise denotes them as trustworthy
  874. // (`delegate_treats_url_as_trustworthy`).
  875. bool is_allowed_to_access_secure_cookies = false;
  876. CookieAccessScheme cookie_access_scheme =
  877. cookie_util::ProvisionalAccessScheme(url);
  878. if (cookie_access_scheme == CookieAccessScheme::kNonCryptographic &&
  879. params.delegate_treats_url_as_trustworthy) {
  880. cookie_access_scheme = CookieAccessScheme::kTrustworthy;
  881. }
  882. switch (cookie_access_scheme) {
  883. case CookieAccessScheme::kNonCryptographic:
  884. if (IsSecure())
  885. status.AddExclusionReason(CookieInclusionStatus::EXCLUDE_SECURE_ONLY);
  886. break;
  887. case CookieAccessScheme::kTrustworthy:
  888. is_allowed_to_access_secure_cookies = true;
  889. if (IsSecure()) {
  890. status.AddWarningReason(
  891. CookieInclusionStatus::
  892. WARN_SECURE_ACCESS_GRANTED_NON_CRYPTOGRAPHIC);
  893. }
  894. break;
  895. case CookieAccessScheme::kCryptographic:
  896. is_allowed_to_access_secure_cookies = true;
  897. break;
  898. }
  899. // Don't include cookies for requests that don't apply to the cookie domain.
  900. if (!IsDomainMatch(url.host()))
  901. status.AddExclusionReason(CookieInclusionStatus::EXCLUDE_DOMAIN_MISMATCH);
  902. // Don't include cookies for requests with a url path that does not path
  903. // match the cookie-path.
  904. if (!IsOnPath(url.path()))
  905. status.AddExclusionReason(CookieInclusionStatus::EXCLUDE_NOT_ON_PATH);
  906. // For LEGACY cookies we should always return the schemeless context,
  907. // otherwise let GetContextForCookieInclusion() decide.
  908. CookieOptions::SameSiteCookieContext::ContextType cookie_inclusion_context =
  909. params.access_semantics == CookieAccessSemantics::LEGACY
  910. ? options.same_site_cookie_context().context()
  911. : options.same_site_cookie_context().GetContextForCookieInclusion();
  912. // Don't include same-site cookies for cross-site requests.
  913. CookieEffectiveSameSite effective_same_site =
  914. GetEffectiveSameSite(params.access_semantics);
  915. DCHECK(effective_same_site != CookieEffectiveSameSite::UNDEFINED);
  916. UMA_HISTOGRAM_ENUMERATION(
  917. "Cookie.RequestSameSiteContext", cookie_inclusion_context,
  918. CookieOptions::SameSiteCookieContext::ContextType::COUNT);
  919. switch (effective_same_site) {
  920. case CookieEffectiveSameSite::STRICT_MODE:
  921. if (cookie_inclusion_context <
  922. CookieOptions::SameSiteCookieContext::ContextType::SAME_SITE_STRICT) {
  923. status.AddExclusionReason(
  924. CookieInclusionStatus::EXCLUDE_SAMESITE_STRICT);
  925. }
  926. break;
  927. case CookieEffectiveSameSite::LAX_MODE:
  928. if (cookie_inclusion_context <
  929. CookieOptions::SameSiteCookieContext::ContextType::SAME_SITE_LAX) {
  930. status.AddExclusionReason(
  931. (SameSite() == CookieSameSite::UNSPECIFIED)
  932. ? CookieInclusionStatus::
  933. EXCLUDE_SAMESITE_UNSPECIFIED_TREATED_AS_LAX
  934. : CookieInclusionStatus::EXCLUDE_SAMESITE_LAX);
  935. }
  936. break;
  937. // TODO(crbug.com/990439): Add a browsertest for this behavior.
  938. case CookieEffectiveSameSite::LAX_MODE_ALLOW_UNSAFE:
  939. DCHECK(SameSite() == CookieSameSite::UNSPECIFIED);
  940. if (cookie_inclusion_context <
  941. CookieOptions::SameSiteCookieContext::ContextType::
  942. SAME_SITE_LAX_METHOD_UNSAFE) {
  943. // TODO(chlily): Do we need a separate CookieInclusionStatus for this?
  944. status.AddExclusionReason(
  945. CookieInclusionStatus::EXCLUDE_SAMESITE_UNSPECIFIED_TREATED_AS_LAX);
  946. }
  947. break;
  948. default:
  949. break;
  950. }
  951. // Unless legacy access semantics are in effect, SameSite=None cookies without
  952. // the Secure attribute should be ignored. This can apply to cookies which
  953. // were created before "SameSite=None requires Secure" was enabled (as
  954. // SameSite=None insecure cookies cannot be set while the options are on).
  955. if (params.access_semantics != CookieAccessSemantics::LEGACY &&
  956. SameSite() == CookieSameSite::NO_RESTRICTION && !IsSecure()) {
  957. status.AddExclusionReason(
  958. CookieInclusionStatus::EXCLUDE_SAMESITE_NONE_INSECURE);
  959. }
  960. switch (params.same_party_status) {
  961. case CookieSamePartyStatus::kEnforceSamePartyExclude:
  962. DCHECK(IsSameParty());
  963. status.AddExclusionReason(
  964. CookieInclusionStatus::EXCLUDE_SAMEPARTY_CROSS_PARTY_CONTEXT);
  965. [[fallthrough]];
  966. case CookieSamePartyStatus::kEnforceSamePartyInclude: {
  967. status.AddWarningReason(CookieInclusionStatus::WARN_TREATED_AS_SAMEPARTY);
  968. // Remove any SameSite exclusion reasons, since SameParty overrides
  969. // SameSite.
  970. DCHECK(!status.HasExclusionReason(
  971. CookieInclusionStatus::EXCLUDE_SAMESITE_STRICT));
  972. DCHECK_NE(effective_same_site, CookieEffectiveSameSite::STRICT_MODE);
  973. bool included_by_samesite =
  974. !status.HasExclusionReason(
  975. CookieInclusionStatus::EXCLUDE_SAMESITE_LAX) &&
  976. !status.HasExclusionReason(
  977. CookieInclusionStatus::
  978. EXCLUDE_SAMESITE_UNSPECIFIED_TREATED_AS_LAX);
  979. if (!included_by_samesite) {
  980. status.RemoveExclusionReasons({
  981. CookieInclusionStatus::EXCLUDE_SAMESITE_LAX,
  982. CookieInclusionStatus::EXCLUDE_SAMESITE_UNSPECIFIED_TREATED_AS_LAX,
  983. });
  984. }
  985. // Update metrics.
  986. if (status.HasOnlyExclusionReason(
  987. CookieInclusionStatus::EXCLUDE_SAMEPARTY_CROSS_PARTY_CONTEXT) &&
  988. included_by_samesite) {
  989. status.AddWarningReason(
  990. CookieInclusionStatus::WARN_SAMEPARTY_EXCLUSION_OVERRULED_SAMESITE);
  991. }
  992. if (status.IsInclude()) {
  993. UMA_HISTOGRAM_BOOLEAN(
  994. "Cookie.SamePartyReadIncluded.InclusionUnderSameSite",
  995. included_by_samesite);
  996. if (!included_by_samesite) {
  997. status.AddWarningReason(
  998. CookieInclusionStatus::
  999. WARN_SAMEPARTY_INCLUSION_OVERRULED_SAMESITE);
  1000. }
  1001. }
  1002. break;
  1003. }
  1004. case CookieSamePartyStatus::kNoSamePartyEnforcement:
  1005. // Only apply SameSite-related warnings if SameParty is not in effect.
  1006. ApplySameSiteCookieWarningToStatus(
  1007. SameSite(), effective_same_site, IsSecure(),
  1008. options.same_site_cookie_context(), &status,
  1009. false /* is_cookie_being_set */);
  1010. break;
  1011. }
  1012. if (status.IsInclude()) {
  1013. UMA_HISTOGRAM_ENUMERATION("Cookie.IncludedRequestEffectiveSameSite",
  1014. effective_same_site,
  1015. CookieEffectiveSameSite::COUNT);
  1016. }
  1017. using ContextRedirectTypeBug1221316 = CookieOptions::SameSiteCookieContext::
  1018. ContextMetadata::ContextRedirectTypeBug1221316;
  1019. ContextRedirectTypeBug1221316 redirect_type_for_metrics =
  1020. options.same_site_cookie_context()
  1021. .GetMetadataForCurrentSchemefulMode()
  1022. .redirect_type_bug_1221316;
  1023. if (redirect_type_for_metrics != ContextRedirectTypeBug1221316::kUnset) {
  1024. UMA_HISTOGRAM_ENUMERATION("Cookie.CrossSiteRedirectType.Read",
  1025. redirect_type_for_metrics);
  1026. }
  1027. if (status.HasWarningReason(
  1028. CookieInclusionStatus::
  1029. WARN_CROSS_SITE_REDIRECT_DOWNGRADE_CHANGES_INCLUSION)) {
  1030. UMA_HISTOGRAM_ENUMERATION(
  1031. "Cookie.CrossSiteRedirectDowngradeChangesInclusion2.Read",
  1032. CookieSameSiteToCookieSameSiteForMetrics(SameSite()));
  1033. }
  1034. return CookieAccessResult(effective_same_site, status,
  1035. params.access_semantics,
  1036. is_allowed_to_access_secure_cookies);
  1037. }
  1038. CookieAccessResult CanonicalCookie::IsSetPermittedInContext(
  1039. const GURL& source_url,
  1040. const CookieOptions& options,
  1041. const CookieAccessParams& params,
  1042. const std::vector<std::string>& cookieable_schemes,
  1043. const absl::optional<CookieAccessResult>& cookie_access_result) const {
  1044. CookieAccessResult access_result;
  1045. if (cookie_access_result) {
  1046. access_result = *cookie_access_result;
  1047. }
  1048. if (!base::Contains(cookieable_schemes, source_url.scheme())) {
  1049. access_result.status.AddExclusionReason(
  1050. CookieInclusionStatus::EXCLUDE_NONCOOKIEABLE_SCHEME);
  1051. }
  1052. CookieAccessScheme access_scheme =
  1053. cookie_util::ProvisionalAccessScheme(source_url);
  1054. if (access_scheme == CookieAccessScheme::kNonCryptographic &&
  1055. params.delegate_treats_url_as_trustworthy) {
  1056. access_scheme = CookieAccessScheme::kTrustworthy;
  1057. }
  1058. switch (access_scheme) {
  1059. case CookieAccessScheme::kNonCryptographic:
  1060. access_result.is_allowed_to_access_secure_cookies = false;
  1061. if (IsSecure()) {
  1062. access_result.status.AddExclusionReason(
  1063. CookieInclusionStatus::EXCLUDE_SECURE_ONLY);
  1064. }
  1065. break;
  1066. case CookieAccessScheme::kCryptographic:
  1067. // All cool!
  1068. access_result.is_allowed_to_access_secure_cookies = true;
  1069. break;
  1070. case CookieAccessScheme::kTrustworthy:
  1071. access_result.is_allowed_to_access_secure_cookies = true;
  1072. if (IsSecure()) {
  1073. // OK, but want people aware of this.
  1074. access_result.status.AddWarningReason(
  1075. CookieInclusionStatus::
  1076. WARN_SECURE_ACCESS_GRANTED_NON_CRYPTOGRAPHIC);
  1077. }
  1078. break;
  1079. }
  1080. access_result.access_semantics = params.access_semantics;
  1081. if (options.exclude_httponly() && IsHttpOnly()) {
  1082. DVLOG(net::cookie_util::kVlogSetCookies)
  1083. << "HttpOnly cookie not permitted in script context.";
  1084. access_result.status.AddExclusionReason(
  1085. CookieInclusionStatus::EXCLUDE_HTTP_ONLY);
  1086. }
  1087. // Unless legacy access semantics are in effect, SameSite=None cookies without
  1088. // the Secure attribute will be rejected.
  1089. if (params.access_semantics != CookieAccessSemantics::LEGACY &&
  1090. SameSite() == CookieSameSite::NO_RESTRICTION && !IsSecure()) {
  1091. DVLOG(net::cookie_util::kVlogSetCookies)
  1092. << "SetCookie() rejecting insecure cookie with SameSite=None.";
  1093. access_result.status.AddExclusionReason(
  1094. CookieInclusionStatus::EXCLUDE_SAMESITE_NONE_INSECURE);
  1095. }
  1096. // For LEGACY cookies we should always return the schemeless context,
  1097. // otherwise let GetContextForCookieInclusion() decide.
  1098. CookieOptions::SameSiteCookieContext::ContextType cookie_inclusion_context =
  1099. params.access_semantics == CookieAccessSemantics::LEGACY
  1100. ? options.same_site_cookie_context().context()
  1101. : options.same_site_cookie_context().GetContextForCookieInclusion();
  1102. access_result.effective_same_site =
  1103. GetEffectiveSameSite(params.access_semantics);
  1104. DCHECK(access_result.effective_same_site !=
  1105. CookieEffectiveSameSite::UNDEFINED);
  1106. switch (access_result.effective_same_site) {
  1107. case CookieEffectiveSameSite::STRICT_MODE:
  1108. // This intentionally checks for `< SAME_SITE_LAX`, as we allow
  1109. // `SameSite=Strict` cookies to be set for top-level navigations that
  1110. // qualify for receipt of `SameSite=Lax` cookies.
  1111. if (cookie_inclusion_context <
  1112. CookieOptions::SameSiteCookieContext::ContextType::SAME_SITE_LAX) {
  1113. DVLOG(net::cookie_util::kVlogSetCookies)
  1114. << "Trying to set a `SameSite=Strict` cookie from a "
  1115. "cross-site URL.";
  1116. access_result.status.AddExclusionReason(
  1117. CookieInclusionStatus::EXCLUDE_SAMESITE_STRICT);
  1118. }
  1119. break;
  1120. case CookieEffectiveSameSite::LAX_MODE:
  1121. case CookieEffectiveSameSite::LAX_MODE_ALLOW_UNSAFE:
  1122. if (cookie_inclusion_context <
  1123. CookieOptions::SameSiteCookieContext::ContextType::SAME_SITE_LAX) {
  1124. if (SameSite() == CookieSameSite::UNSPECIFIED) {
  1125. DVLOG(net::cookie_util::kVlogSetCookies)
  1126. << "Cookies with no known SameSite attribute being treated as "
  1127. "lax; attempt to set from a cross-site URL denied.";
  1128. access_result.status.AddExclusionReason(
  1129. CookieInclusionStatus::
  1130. EXCLUDE_SAMESITE_UNSPECIFIED_TREATED_AS_LAX);
  1131. } else {
  1132. DVLOG(net::cookie_util::kVlogSetCookies)
  1133. << "Trying to set a `SameSite=Lax` cookie from a cross-site URL.";
  1134. access_result.status.AddExclusionReason(
  1135. CookieInclusionStatus::EXCLUDE_SAMESITE_LAX);
  1136. }
  1137. }
  1138. break;
  1139. default:
  1140. break;
  1141. }
  1142. switch (params.same_party_status) {
  1143. case CookieSamePartyStatus::kEnforceSamePartyExclude:
  1144. DCHECK(IsSameParty());
  1145. access_result.status.AddExclusionReason(
  1146. CookieInclusionStatus::EXCLUDE_SAMEPARTY_CROSS_PARTY_CONTEXT);
  1147. [[fallthrough]];
  1148. case CookieSamePartyStatus::kEnforceSamePartyInclude: {
  1149. DCHECK(IsSameParty());
  1150. access_result.status.AddWarningReason(
  1151. CookieInclusionStatus::WARN_TREATED_AS_SAMEPARTY);
  1152. // Remove any SameSite exclusion reasons, since SameParty overrides
  1153. // SameSite.
  1154. DCHECK(!access_result.status.HasExclusionReason(
  1155. CookieInclusionStatus::EXCLUDE_SAMESITE_STRICT));
  1156. DCHECK_NE(access_result.effective_same_site,
  1157. CookieEffectiveSameSite::STRICT_MODE);
  1158. bool included_by_samesite =
  1159. !access_result.status.HasExclusionReason(
  1160. CookieInclusionStatus::EXCLUDE_SAMESITE_LAX) &&
  1161. !access_result.status.HasExclusionReason(
  1162. CookieInclusionStatus::
  1163. EXCLUDE_SAMESITE_UNSPECIFIED_TREATED_AS_LAX);
  1164. if (!included_by_samesite) {
  1165. access_result.status.RemoveExclusionReasons({
  1166. CookieInclusionStatus::EXCLUDE_SAMESITE_LAX,
  1167. CookieInclusionStatus::EXCLUDE_SAMESITE_UNSPECIFIED_TREATED_AS_LAX,
  1168. });
  1169. }
  1170. // Update metrics.
  1171. if (access_result.status.HasOnlyExclusionReason(
  1172. CookieInclusionStatus::EXCLUDE_SAMEPARTY_CROSS_PARTY_CONTEXT) &&
  1173. included_by_samesite) {
  1174. access_result.status.AddWarningReason(
  1175. CookieInclusionStatus::WARN_SAMEPARTY_EXCLUSION_OVERRULED_SAMESITE);
  1176. }
  1177. if (access_result.status.IsInclude()) {
  1178. UMA_HISTOGRAM_BOOLEAN(
  1179. "Cookie.SamePartySetIncluded.InclusionUnderSameSite",
  1180. included_by_samesite);
  1181. if (!included_by_samesite) {
  1182. access_result.status.AddWarningReason(
  1183. CookieInclusionStatus::
  1184. WARN_SAMEPARTY_INCLUSION_OVERRULED_SAMESITE);
  1185. }
  1186. }
  1187. break;
  1188. }
  1189. case CookieSamePartyStatus::kNoSamePartyEnforcement:
  1190. // Only apply SameSite-related warnings if SameParty is not in effect.
  1191. ApplySameSiteCookieWarningToStatus(
  1192. SameSite(), access_result.effective_same_site, IsSecure(),
  1193. options.same_site_cookie_context(), &access_result.status,
  1194. true /* is_cookie_being_set */);
  1195. break;
  1196. }
  1197. if (access_result.status.IsInclude()) {
  1198. UMA_HISTOGRAM_ENUMERATION("Cookie.IncludedResponseEffectiveSameSite",
  1199. access_result.effective_same_site,
  1200. CookieEffectiveSameSite::COUNT);
  1201. }
  1202. using ContextRedirectTypeBug1221316 = CookieOptions::SameSiteCookieContext::
  1203. ContextMetadata::ContextRedirectTypeBug1221316;
  1204. ContextRedirectTypeBug1221316 redirect_type_for_metrics =
  1205. options.same_site_cookie_context()
  1206. .GetMetadataForCurrentSchemefulMode()
  1207. .redirect_type_bug_1221316;
  1208. if (redirect_type_for_metrics != ContextRedirectTypeBug1221316::kUnset) {
  1209. UMA_HISTOGRAM_ENUMERATION("Cookie.CrossSiteRedirectType.Write",
  1210. redirect_type_for_metrics);
  1211. }
  1212. if (access_result.status.HasWarningReason(
  1213. CookieInclusionStatus::
  1214. WARN_CROSS_SITE_REDIRECT_DOWNGRADE_CHANGES_INCLUSION)) {
  1215. UMA_HISTOGRAM_ENUMERATION(
  1216. "Cookie.CrossSiteRedirectDowngradeChangesInclusion2.Write",
  1217. CookieSameSiteToCookieSameSiteForMetrics(SameSite()));
  1218. }
  1219. return access_result;
  1220. }
  1221. std::string CanonicalCookie::DebugString() const {
  1222. return base::StringPrintf(
  1223. "name: %s value: %s domain: %s path: %s creation: %" PRId64,
  1224. name_.c_str(), value_.c_str(), domain_.c_str(), path_.c_str(),
  1225. static_cast<int64_t>(creation_date_.ToTimeT()));
  1226. }
  1227. bool CanonicalCookie::PartialCompare(const CanonicalCookie& other) const {
  1228. return PartialCookieOrdering(*this, other) < 0;
  1229. }
  1230. bool CanonicalCookie::IsCanonical() const {
  1231. // TODO(crbug.com/1244172) Eventually we should check the size of name+value,
  1232. // assuming we collect metrics and determine that a low percentage of cookies
  1233. // would fail this check. Note that we still don't want to enforce length
  1234. // checks on domain or path for the reason stated above.
  1235. // TODO(crbug.com/1264458): Eventually we should push this logic into
  1236. // IsCanonicalForFromStorage, but for now we allow cookies already stored with
  1237. // high expiration dates to be retrieved.
  1238. if (ValidateAndAdjustExpiryDate(expiry_date_, creation_date_) != expiry_date_)
  1239. return false;
  1240. return IsCanonicalForFromStorage();
  1241. }
  1242. bool CanonicalCookie::IsCanonicalForFromStorage() const {
  1243. // Not checking domain or path against ParsedCookie as it may have
  1244. // come purely from the URL. Also, don't call IsValidCookieNameValuePair()
  1245. // here because we don't want to enforce the size checks on names or values
  1246. // that may have been reconstituted from the cookie store.
  1247. if (ParsedCookie::ParseTokenString(name_) != name_ ||
  1248. !ParsedCookie::ValueMatchesParsedValue(value_)) {
  1249. return false;
  1250. }
  1251. if (base::FeatureList::IsEnabled(features::kExtraCookieValidityChecks)) {
  1252. if (!ParsedCookie::IsValidCookieName(name_) ||
  1253. !ParsedCookie::IsValidCookieValue(value_)) {
  1254. return false;
  1255. }
  1256. } else {
  1257. if (!ParsedCookie::IsValidCookieAttributeValueLegacy(name_) ||
  1258. !ParsedCookie::IsValidCookieAttributeValueLegacy(value_)) {
  1259. return false;
  1260. }
  1261. }
  1262. if (!last_access_date_.is_null() && creation_date_.is_null())
  1263. return false;
  1264. url::CanonHostInfo canon_host_info;
  1265. std::string canonical_domain(CanonicalizeHost(domain_, &canon_host_info));
  1266. // TODO(rdsmith): This specifically allows for empty domains. The spec
  1267. // suggests this is invalid (if a domain attribute is empty, the cookie's
  1268. // domain is set to the canonicalized request host; see
  1269. // https://tools.ietf.org/html/rfc6265#section-5.3). However, it is
  1270. // needed for Chrome extension cookies.
  1271. // See http://crbug.com/730633 for more information.
  1272. if (canonical_domain != domain_)
  1273. return false;
  1274. if (path_.empty() || path_[0] != '/')
  1275. return false;
  1276. CookiePrefix prefix = GetCookiePrefix(name_);
  1277. switch (prefix) {
  1278. case COOKIE_PREFIX_HOST:
  1279. if (!secure_ || path_ != "/" || domain_.empty() || domain_[0] == '.')
  1280. return false;
  1281. break;
  1282. case COOKIE_PREFIX_SECURE:
  1283. if (!secure_)
  1284. return false;
  1285. break;
  1286. default:
  1287. break;
  1288. }
  1289. if (name_ == "" && HasHiddenPrefixName(value_))
  1290. return false;
  1291. if (!IsCookieSamePartyValid(same_party_, secure_, same_site_))
  1292. return false;
  1293. if (IsPartitioned()) {
  1294. if (CookiePartitionKey::HasNonce(partition_key_))
  1295. return true;
  1296. if (!secure_)
  1297. return false;
  1298. }
  1299. return true;
  1300. }
  1301. bool CanonicalCookie::IsEffectivelySameSiteNone(
  1302. CookieAccessSemantics access_semantics) const {
  1303. return GetEffectiveSameSite(access_semantics) ==
  1304. CookieEffectiveSameSite::NO_RESTRICTION;
  1305. }
  1306. CookieEffectiveSameSite CanonicalCookie::GetEffectiveSameSiteForTesting(
  1307. CookieAccessSemantics access_semantics) const {
  1308. return GetEffectiveSameSite(access_semantics);
  1309. }
  1310. // static
  1311. std::string CanonicalCookie::BuildCookieLine(const CookieList& cookies) {
  1312. std::string cookie_line;
  1313. for (const auto& cookie : cookies) {
  1314. AppendCookieLineEntry(cookie, &cookie_line);
  1315. }
  1316. return cookie_line;
  1317. }
  1318. // static
  1319. std::string CanonicalCookie::BuildCookieLine(
  1320. const CookieAccessResultList& cookie_access_result_list) {
  1321. std::string cookie_line;
  1322. for (const auto& cookie_with_access_result : cookie_access_result_list) {
  1323. const CanonicalCookie& cookie = cookie_with_access_result.cookie;
  1324. AppendCookieLineEntry(cookie, &cookie_line);
  1325. }
  1326. return cookie_line;
  1327. }
  1328. // static
  1329. std::string CanonicalCookie::BuildCookieAttributesLine(
  1330. const CanonicalCookie& cookie) {
  1331. std::string cookie_line;
  1332. // In Mozilla, if you set a cookie like "AAA", it will have an empty token
  1333. // and a value of "AAA". When it sends the cookie back, it will send "AAA",
  1334. // so we need to avoid sending "=AAA" for a blank token value.
  1335. if (!cookie.Name().empty())
  1336. cookie_line += cookie.Name() + "=";
  1337. cookie_line += cookie.Value();
  1338. if (!cookie.Domain().empty())
  1339. cookie_line += "; domain=" + cookie.Domain();
  1340. if (!cookie.Path().empty())
  1341. cookie_line += "; path=" + cookie.Path();
  1342. if (cookie.ExpiryDate() != base::Time())
  1343. cookie_line += "; expires=" + TimeFormatHTTP(cookie.ExpiryDate());
  1344. if (cookie.IsSecure())
  1345. cookie_line += "; secure";
  1346. if (cookie.IsHttpOnly())
  1347. cookie_line += "; httponly";
  1348. switch (cookie.SameSite()) {
  1349. case CookieSameSite::NO_RESTRICTION:
  1350. cookie_line += "; samesite=none";
  1351. break;
  1352. case CookieSameSite::LAX_MODE:
  1353. cookie_line += "; samesite=lax";
  1354. break;
  1355. case CookieSameSite::STRICT_MODE:
  1356. cookie_line += "; samesite=strict";
  1357. break;
  1358. case CookieSameSite::UNSPECIFIED:
  1359. // Don't append any text if the samesite attribute wasn't explicitly set.
  1360. break;
  1361. }
  1362. return cookie_line;
  1363. }
  1364. // static
  1365. CanonicalCookie::CookiePrefix CanonicalCookie::GetCookiePrefix(
  1366. const std::string& name) {
  1367. const char kSecurePrefix[] = "__Secure-";
  1368. const char kHostPrefix[] = "__Host-";
  1369. if (base::StartsWith(name, kSecurePrefix, base::CompareCase::SENSITIVE))
  1370. return CanonicalCookie::COOKIE_PREFIX_SECURE;
  1371. if (base::StartsWith(name, kHostPrefix, base::CompareCase::SENSITIVE))
  1372. return CanonicalCookie::COOKIE_PREFIX_HOST;
  1373. return CanonicalCookie::COOKIE_PREFIX_NONE;
  1374. }
  1375. // static
  1376. void CanonicalCookie::RecordCookiePrefixMetrics(
  1377. CanonicalCookie::CookiePrefix prefix,
  1378. bool is_cookie_valid) {
  1379. const char kCookiePrefixHistogram[] = "Cookie.CookiePrefix";
  1380. UMA_HISTOGRAM_ENUMERATION(kCookiePrefixHistogram, prefix,
  1381. CanonicalCookie::COOKIE_PREFIX_LAST);
  1382. }
  1383. // Returns true if the cookie does not violate any constraints imposed
  1384. // by the cookie name's prefix, as described in
  1385. // https://tools.ietf.org/html/draft-west-cookie-prefixes
  1386. //
  1387. // static
  1388. bool CanonicalCookie::IsCookiePrefixValid(CanonicalCookie::CookiePrefix prefix,
  1389. const GURL& url,
  1390. const ParsedCookie& parsed_cookie) {
  1391. return CanonicalCookie::IsCookiePrefixValid(
  1392. prefix, url, parsed_cookie.IsSecure(),
  1393. parsed_cookie.HasDomain() ? parsed_cookie.Domain() : "",
  1394. parsed_cookie.HasPath() ? parsed_cookie.Path() : "");
  1395. }
  1396. bool CanonicalCookie::IsCookiePrefixValid(CanonicalCookie::CookiePrefix prefix,
  1397. const GURL& url,
  1398. bool secure,
  1399. const std::string& domain,
  1400. const std::string& path) {
  1401. if (prefix == CanonicalCookie::COOKIE_PREFIX_SECURE)
  1402. return secure && url.SchemeIsCryptographic();
  1403. if (prefix == CanonicalCookie::COOKIE_PREFIX_HOST) {
  1404. return HasValidHostPrefixAttributes(url, secure, domain, path);
  1405. }
  1406. return true;
  1407. }
  1408. CookieEffectiveSameSite CanonicalCookie::GetEffectiveSameSite(
  1409. CookieAccessSemantics access_semantics) const {
  1410. base::TimeDelta lax_allow_unsafe_threshold_age =
  1411. base::FeatureList::IsEnabled(
  1412. features::kSameSiteDefaultChecksMethodRigorously)
  1413. ? base::TimeDelta::Min()
  1414. : (base::FeatureList::IsEnabled(
  1415. features::kShortLaxAllowUnsafeThreshold)
  1416. ? kShortLaxAllowUnsafeMaxAge
  1417. : kLaxAllowUnsafeMaxAge);
  1418. switch (SameSite()) {
  1419. // If a cookie does not have a SameSite attribute, the effective SameSite
  1420. // mode depends on the access semantics and whether the cookie is
  1421. // recently-created.
  1422. case CookieSameSite::UNSPECIFIED:
  1423. return (access_semantics == CookieAccessSemantics::LEGACY)
  1424. ? CookieEffectiveSameSite::NO_RESTRICTION
  1425. : (IsRecentlyCreated(lax_allow_unsafe_threshold_age)
  1426. ? CookieEffectiveSameSite::LAX_MODE_ALLOW_UNSAFE
  1427. : CookieEffectiveSameSite::LAX_MODE);
  1428. case CookieSameSite::NO_RESTRICTION:
  1429. return CookieEffectiveSameSite::NO_RESTRICTION;
  1430. case CookieSameSite::LAX_MODE:
  1431. return CookieEffectiveSameSite::LAX_MODE;
  1432. case CookieSameSite::STRICT_MODE:
  1433. return CookieEffectiveSameSite::STRICT_MODE;
  1434. }
  1435. }
  1436. // static
  1437. bool CanonicalCookie::HasHiddenPrefixName(
  1438. const base::StringPiece cookie_value) {
  1439. // Skip BWS as defined by HTTPSEM as SP or HTAB (0x20 or 0x9).
  1440. base::StringPiece value_without_BWS =
  1441. base::TrimString(cookie_value, " \t", base::TRIM_LEADING);
  1442. const base::StringPiece host_prefix = "__Host-";
  1443. // Compare the value to the host_prefix.
  1444. if (base::StartsWith(value_without_BWS, host_prefix)) {
  1445. // The prefix matches, now check if the value string contains a subsequent
  1446. // '='.
  1447. if (value_without_BWS.find_first_of('=', host_prefix.size()) !=
  1448. base::StringPiece::npos) {
  1449. // This value contains a hidden prefix name.
  1450. return true;
  1451. }
  1452. return false;
  1453. }
  1454. // Do a similar check for the secure prefix
  1455. const base::StringPiece secure_prefix = "__Secure-";
  1456. if (base::StartsWith(value_without_BWS, secure_prefix)) {
  1457. if (value_without_BWS.find_first_of('=', secure_prefix.size()) !=
  1458. base::StringPiece::npos) {
  1459. return true;
  1460. }
  1461. }
  1462. return false;
  1463. }
  1464. bool CanonicalCookie::IsRecentlyCreated(base::TimeDelta age_threshold) const {
  1465. return (base::Time::Now() - creation_date_) <= age_threshold;
  1466. }
  1467. // static
  1468. bool CanonicalCookie::IsCookieSamePartyValid(
  1469. const ParsedCookie& parsed_cookie) {
  1470. return IsCookieSamePartyValid(parsed_cookie.IsSameParty(),
  1471. parsed_cookie.IsSecure(),
  1472. parsed_cookie.SameSite());
  1473. }
  1474. // static
  1475. bool CanonicalCookie::IsCookieSamePartyValid(bool is_same_party,
  1476. bool is_secure,
  1477. CookieSameSite same_site) {
  1478. if (!is_same_party)
  1479. return true;
  1480. return is_secure && (same_site != CookieSameSite::STRICT_MODE);
  1481. }
  1482. // static
  1483. bool CanonicalCookie::IsCookiePartitionedValid(
  1484. const GURL& url,
  1485. const ParsedCookie& parsed_cookie,
  1486. bool partition_has_nonce) {
  1487. return IsCookiePartitionedValid(
  1488. url, /*secure=*/parsed_cookie.IsSecure(),
  1489. /*is_partitioned=*/parsed_cookie.IsPartitioned(), partition_has_nonce);
  1490. }
  1491. // static
  1492. bool CanonicalCookie::IsCookiePartitionedValid(const GURL& url,
  1493. bool secure,
  1494. bool is_partitioned,
  1495. bool partition_has_nonce) {
  1496. if (!is_partitioned)
  1497. return true;
  1498. if (partition_has_nonce)
  1499. return true;
  1500. bool result = url.SchemeIsCryptographic() && secure;
  1501. DLOG_IF(WARNING, !result)
  1502. << "CanonicalCookie has invalid Partitioned attribute";
  1503. return result;
  1504. }
  1505. CookieAndLineWithAccessResult::CookieAndLineWithAccessResult() = default;
  1506. CookieAndLineWithAccessResult::CookieAndLineWithAccessResult(
  1507. absl::optional<CanonicalCookie> cookie,
  1508. std::string cookie_string,
  1509. CookieAccessResult access_result)
  1510. : cookie(std::move(cookie)),
  1511. cookie_string(std::move(cookie_string)),
  1512. access_result(access_result) {}
  1513. CookieAndLineWithAccessResult::CookieAndLineWithAccessResult(
  1514. const CookieAndLineWithAccessResult&) = default;
  1515. CookieAndLineWithAccessResult& CookieAndLineWithAccessResult::operator=(
  1516. const CookieAndLineWithAccessResult& cookie_and_line_with_access_result) =
  1517. default;
  1518. CookieAndLineWithAccessResult::CookieAndLineWithAccessResult(
  1519. CookieAndLineWithAccessResult&&) = default;
  1520. CookieAndLineWithAccessResult::~CookieAndLineWithAccessResult() = default;
  1521. } // namespace net