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 #ifndef NET_COOKIES_COOKIE_BASE_H_ 6 #define NET_COOKIES_COOKIE_BASE_H_ 7 8 #include <optional> 9 #include <string> 10 #include <tuple> 11 12 #include "net/base/net_export.h" 13 #include "net/cookies/cookie_access_params.h" 14 #include "net/cookies/cookie_access_result.h" 15 #include "net/cookies/cookie_constants.h" 16 #include "net/cookies/cookie_options.h" 17 #include "net/cookies/cookie_partition_key.h" 18 19 class GURL; 20 21 namespace net { 22 23 // A base class for cookies and cookie-like objects. Encapsulates logic for 24 // determining whether a cookie could be sent/set, based on its attributes and 25 // the request context. 26 class NET_EXPORT CookieBase { 27 public: 28 // StrictlyUniqueCookieKey always populates the cookie's source scheme and 29 // source port. 30 using StrictlyUniqueCookieKey = std::tuple<std::optional<CookiePartitionKey>, 31 /*name=*/std::string, 32 /*domain=*/std::string, 33 /*path=*/std::string, 34 CookieSourceScheme, 35 /*source_port=*/int>; 36 37 // Conditionally populates the source scheme and source port depending on the 38 // state of their associated feature. 39 using UniqueCookieKey = std::tuple<std::optional<CookiePartitionKey>, 40 /*name=*/std::string, 41 /*domain=*/std::string, 42 /*path=*/std::string, 43 std::optional<CookieSourceScheme>, 44 /*source_port=*/std::optional<int>>; 45 46 // Same as UniqueCookieKey but for use with Domain cookies, which do not 47 // consider the source_port. 48 using UniqueDomainCookieKey = std::tuple<std::optional<CookiePartitionKey>, 49 /*name=*/std::string, 50 /*domain=*/std::string, 51 /*path=*/std::string, 52 std::optional<CookieSourceScheme>>; 53 54 // Returns if the cookie should be included (and if not, why) for the given 55 // request |url| using the CookieInclusionStatus enum. HTTP only cookies can 56 // be filter by using appropriate cookie |options|. 57 // 58 // PLEASE NOTE that this method does not check whether a cookie is expired or 59 // not! 60 CookieAccessResult IncludeForRequestURL( 61 const GURL& url, 62 const CookieOptions& options, 63 const CookieAccessParams& params) const; 64 65 // Returns if the cookie with given attributes can be set in context described 66 // by |options| and |params|, and if no, describes why. 67 // 68 // |cookie_access_result| is an optional input status, to allow for status 69 // chaining from callers. It helps callers provide the status of a 70 // cookie that may have warnings associated with it. 71 CookieAccessResult IsSetPermittedInContext( 72 const GURL& source_url, 73 const CookieOptions& options, 74 const CookieAccessParams& params, 75 const std::vector<std::string>& cookieable_schemes, 76 const std::optional<CookieAccessResult>& cookie_access_result = 77 std::nullopt) const; 78 79 // Returns true if the given |url_path| path-matches this cookie's cookie-path 80 // as described in section 5.1.4 in RFC 6265. This returns true if |path_| and 81 // |url_path| are identical, or if |url_path| is a subdirectory of |path_|. 82 bool IsOnPath(const std::string& url_path) const; 83 84 // This returns true if this cookie's |domain_| indicates that it can be 85 // accessed by |host|. 86 // 87 // In the case where |domain_| has no leading dot, this is a host cookie and 88 // will only domain match if |host| is identical to |domain_|. 89 // 90 // In the case where |domain_| has a leading dot, this is a domain cookie. It 91 // will match |host| if |domain_| is a suffix of |host|, or if |domain_| is 92 // exactly equal to |host| plus a leading dot. 93 // 94 // Note that this isn't quite the same as the "domain-match" algorithm in RFC 95 // 6265bis, since our implementation uses the presence of a leading dot in the 96 // |domain_| string in place of the spec's host-only-flag. That is, if 97 // |domain_| has no leading dot, then we only consider it matching if |host| 98 // is identical (which reflects the intended behavior when the cookie has a 99 // host-only-flag), whereas the RFC also treats them as domain-matching if 100 // |domain_| is a subdomain of |host|. 101 bool IsDomainMatch(const std::string& host) const; 102 Name()103 const std::string& Name() const { return name_; } 104 // We represent the cookie's host-only-flag as the absence of a leading dot in 105 // Domain(). See IsDomainCookie() and IsHostCookie() below. 106 // If you want the "cookie's domain" as described in RFC 6265bis, use 107 // DomainWithoutDot(). Domain()108 const std::string& Domain() const { return domain_; } Path()109 const std::string& Path() const { return path_; } CreationDate()110 const base::Time& CreationDate() const { return creation_date_; } SecureAttribute()111 bool SecureAttribute() const { return secure_; } IsHttpOnly()112 bool IsHttpOnly() const { return httponly_; } SameSite()113 CookieSameSite SameSite() const { return same_site_; } 114 115 // Returns true if this cookie can only be accessed in a secure context. 116 bool IsSecure() const; 117 IsPartitioned()118 bool IsPartitioned() const { return partition_key_.has_value(); } PartitionKey()119 const std::optional<CookiePartitionKey>& PartitionKey() const { 120 return partition_key_; 121 } 122 123 // Returns whether this cookie is Partitioned and its partition key matches a 124 // a same-site context by checking if the cookies domain site is the same as 125 // the partition key's site. 126 // This function should not be used for third-party cookie blocking 127 // enforcement-related decisions. That logic should rely on `IsPartitioned`. 128 // These functions are for recording metrics about partitioned cookie usage. 129 // Returns false if the cookie has no partition key. 130 bool IsFirstPartyPartitioned() const; 131 132 // Returns whether the cookie is partitioned in a third-party context. 133 // This function should not be used for third-party cookie blocking 134 // enforcement-related decisions. That logic should rely on `IsPartitioned`. 135 // These functions are for recording metrics about partitioned cookie usage. 136 // Returns false if the cookie has no partition key. 137 bool IsThirdPartyPartitioned() const; 138 139 // Returns an enum indicating the scheme of the origin that 140 // set this cookie. This is not part of the cookie spec but is being used to 141 // collect metrics for a potential change to the cookie spec 142 // (https://tools.ietf.org/html/draft-west-cookie-incrementalism-01#section-3.4) SourceScheme()143 CookieSourceScheme SourceScheme() const { return source_scheme_; } 144 // Returns the port of the origin that originally set this cookie (the 145 // source port). This is not part of the cookie spec but is being used to 146 // collect metrics for a potential change to the cookie spec. SourcePort()147 int SourcePort() const { return source_port_; } 148 IsDomainCookie()149 bool IsDomainCookie() const { return !domain_.empty() && domain_[0] == '.'; } IsHostCookie()150 bool IsHostCookie() const { return !IsDomainCookie(); } 151 152 // Returns the cookie's domain, with the leading dot removed, if present. 153 // This corresponds to the "cookie's domain" as described in RFC 6265bis. 154 std::string DomainWithoutDot() const; 155 StrictlyUniqueKey()156 StrictlyUniqueCookieKey StrictlyUniqueKey() const { 157 return std::make_tuple(partition_key_, name_, domain_, path_, 158 source_scheme_, source_port_); 159 } 160 161 // Returns a key such that two cookies with the same UniqueKey() are 162 // guaranteed to be equivalent in the sense of IsEquivalent(). 163 // The `partition_key_` field will always be nullopt when partitioned cookies 164 // are not enabled. 165 // The source_scheme and source_port fields depend on whether or not their 166 // associated features are enabled. 167 UniqueCookieKey UniqueKey() const; 168 169 // Same as UniqueKey() except it does not contain a source_port field. For use 170 // with Domain cookies, which do not consider the source_port. 171 UniqueDomainCookieKey UniqueDomainKey() const; 172 SetSourceScheme(CookieSourceScheme source_scheme)173 void SetSourceScheme(CookieSourceScheme source_scheme) { 174 source_scheme_ = source_scheme; 175 } 176 177 // Set the source port value. Performs a range check and sets the port to 178 // url::PORT_INVALID if value isn't in [0,65535] or url::PORT_UNSPECIFIED. 179 void SetSourcePort(int port); 180 SetCreationDate(const base::Time & date)181 void SetCreationDate(const base::Time& date) { creation_date_ = date; } 182 183 protected: 184 CookieBase(); 185 CookieBase(const CookieBase& other); 186 CookieBase(CookieBase&& other); 187 CookieBase& operator=(const CookieBase& other); 188 CookieBase& operator=(CookieBase&& other); 189 virtual ~CookieBase(); 190 191 CookieBase(std::string name, 192 std::string domain, 193 std::string path, 194 base::Time creation, 195 bool secure, 196 bool httponly, 197 CookieSameSite same_site, 198 std::optional<CookiePartitionKey> partition_key, 199 CookieSourceScheme source_scheme = CookieSourceScheme::kUnset, 200 int source_port = url::PORT_UNSPECIFIED); 201 202 // Returns the effective SameSite mode to apply to this cookie. Depends on the 203 // value of the given SameSite attribute and the access semantics of the 204 // cookie. 205 // Note: If you are converting to a different representation of a cookie, you 206 // probably want to use SameSite() instead of this method. Otherwise, if you 207 // are considering using this method, consider whether you should use 208 // IncludeForRequestURL() or IsSetPermittedInContext() instead of doing the 209 // SameSite computation yourself. 210 CookieEffectiveSameSite GetEffectiveSameSite( 211 CookieAccessSemantics access_semantics) const; 212 213 // Returns whether the cookie was created at most |age_threshold| ago. 214 bool IsRecentlyCreated(base::TimeDelta age_threshold) const; 215 216 // Checks if `port` is within [0,65535] or url::PORT_UNSPECIFIED. Returns 217 // `port` if so and url::PORT_INVALID otherwise. 218 static int ValidateAndAdjustSourcePort(int port); 219 220 private: 221 // Allows subclasses to add custom logic for e.g. logging metrics. Called 222 // after inclusion has been determined for the respective access. PostIncludeForRequestURL(const CookieAccessResult & access_result,const CookieOptions & options_used,CookieOptions::SameSiteCookieContext::ContextType cookie_inclusion_context_used)223 virtual void PostIncludeForRequestURL( 224 const CookieAccessResult& access_result, 225 const CookieOptions& options_used, 226 CookieOptions::SameSiteCookieContext::ContextType 227 cookie_inclusion_context_used) const {} PostIsSetPermittedInContext(const CookieAccessResult & access_result,const CookieOptions & options_used)228 virtual void PostIsSetPermittedInContext( 229 const CookieAccessResult& access_result, 230 const CookieOptions& options_used) const {} 231 232 // Keep defaults here in sync with 233 // services/network/public/interfaces/cookie_manager.mojom. 234 std::string name_; 235 std::string domain_; 236 std::string path_; 237 base::Time creation_date_; 238 bool secure_{false}; 239 bool httponly_{false}; 240 CookieSameSite same_site_{CookieSameSite::NO_RESTRICTION}; 241 // This will be std::nullopt for all cookies not set with the Partitioned 242 // attribute or without a nonce. If the value is non-null, then the cookie 243 // will only be delivered when the top-frame site matches the partition key 244 // and the nonce (if present). If the partition key is non-null and opaque, 245 // this means the Partitioned cookie was created on an opaque origin or with 246 // a nonce. 247 std::optional<CookiePartitionKey> partition_key_; 248 CookieSourceScheme source_scheme_{CookieSourceScheme::kUnset}; 249 // This can be [0,65535], PORT_UNSPECIFIED, or PORT_INVALID. 250 // PORT_UNSPECIFIED is used for cookies which already existed in the cookie 251 // store prior to this change and therefore their port is unknown. 252 // PORT_INVALID is an error for when an out of range port is provided. 253 int source_port_{url::PORT_UNSPECIFIED}; 254 }; 255 256 } // namespace net 257 258 #endif // NET_COOKIES_COOKIE_BASE_H_ 259