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_DEVICE_BOUND_SESSIONS_COOKIE_CRAVING_H_ 6 #define NET_DEVICE_BOUND_SESSIONS_COOKIE_CRAVING_H_ 7 8 #include <optional> 9 #include <string> 10 11 #include "base/time/time.h" 12 #include "net/base/net_export.h" 13 #include "net/cookies/cookie_base.h" 14 #include "net/cookies/cookie_constants.h" 15 #include "net/cookies/cookie_partition_key.h" 16 17 namespace net { 18 class CanonicalCookie; 19 } 20 21 namespace net::device_bound_sessions { 22 23 namespace proto { 24 class CookieCraving; 25 } 26 27 // This class represents the need for a certain cookie to be present. It is not 28 // a cookie itself, but rather represents a requirement which can be satisfied 29 // by a real cookie (i.e. a CanonicalCookie). Each CookieCraving is specified by 30 // and associated with a DBSC (Device Bound Session Credentials) session. 31 // 32 // In general, CookieCraving behavior is intended to be as close as possible to 33 // CanonicalCookie, especially the inclusion logic, since they need to be 34 // matched up. However, some notable differences include: 35 // 36 // CookieCraving does not have a value field, i.e. they only have a name (and 37 // other attributes). The name can be the empty string. The name of a cookie is 38 // needed to identify it, but the value of a cookie is not relevant to its 39 // inclusion or exclusion, so CookieCraving omits it. 40 // 41 // CookieCraving does not have an expiry date. The expiry date of a 42 // CanonicalCookie often depends upon the creation time (if it is set via 43 // Max-Age), and a DBSC session config is not necessarily created at the same 44 // time as its corresponding Set-Cookie header, so we cannot guarantee that 45 // they'd match. DBSC also does not require a specific expiry date for the 46 // cookies whose presence it guarantees. 47 // 48 // CookieCraving does not implement lax-allow-unsafe behavior (it does not 49 // set a non-zero age threshold for it). The default CanonicalCookie 50 // lax-allow-unsafe behavior is problematic because it can result in two 51 // identical set-cookie lines (set from the same URL) exhibiting different 52 // inclusion results, if they happen to be on opposite sides of the 53 // lax-allow-unsafe age threshold. By not implementing lax-allow-unsafe, 54 // CookieCraving may sometimes be excluded even when a corresponding 55 // CanonicalCookie would be included for being under its lax-allow-unsafe age 56 // threshold. This means that servers deploying DBSC with SameSite-unspecified 57 // cookies SHOULD NOT rely on the presence of SameSite-unspecified cookies 58 // within 2 minutes of their creation time on cross-site POST and other unsafe 59 // request types, as DBSC cannot make any such guarantee. 60 class NET_EXPORT CookieCraving : public CookieBase { 61 public: 62 // Creates a new CookieCraving in the context of `url`, given a `name` and 63 // associated cookie `attributes`. (Note that CookieCravings do not have a 64 // "value".) `url` must be valid. `creation_time` may not be null. May return 65 // nullopt if an attribute value is invalid. If a CookieCraving is returned, 66 // it will satisfy IsValid(). If there is leading or trailing whitespace in 67 // `name`, it will get trimmed. 68 // 69 // `cookie_partition_key` only needs to be present if the attributes contain 70 // the Partitioned attribute. std::nullopt indicates an unpartitioned 71 // CookieCraving will be created. If there is a partition key but the 72 // attributes do not specify Partitioned, the resulting CookieCraving will be 73 // unpartitioned. If the partition_key is nullopt, the CookieCraving will 74 // always be unpartitioned even if the attributes specify Partitioned. 75 // 76 // SameSite and HttpOnly related parameters are not checked here, 77 // so creation of CookieCravings with e.g. SameSite=Strict from a cross-site 78 // context is allowed. Create() also does not check whether `url` has a secure 79 // scheme if attempting to create a Secure cookie. The Secure, SameSite, and 80 // HttpOnly related parameters should be checked when deciding CookieCraving 81 // inclusion for a given request/context. 82 // 83 // In general this is intended to closely mirror CanonicalCookie::Create. 84 // However, there are some simplifying assumptions made*, and metrics are not 85 // (currently) logged so as to not interfere with CanonicalCookie metrics. 86 // There is also no (current) need for a CookieInclusionStatus to be returned. 87 // 88 // * Simplifying assumptions (differing from CanonicalCookie): 89 // - The Domain() member of a CookieCraving is required to be non-empty, 90 // which CanonicalCookie does not require. 91 // - Cookie name prefixes (__Host- and __Secure-) are always checked 92 // case-insensitively, unlike CanonicalCookie which reads a Feature value 93 // to decide whether to check insensitively. 94 // - CanonicalCookie allows non-cryptographic URLs to create a cookie with a 95 // secure source_scheme, if that cookie was Secure, on the basis that that 96 // URL might be trustworthy when checked later. CookieCraving does not 97 // allow this. 98 static std::optional<CookieCraving> Create( 99 const GURL& url, 100 const std::string& name, 101 const std::string& attributes, 102 base::Time creation_time, 103 std::optional<CookiePartitionKey> cookie_partition_key); 104 105 CookieCraving(const CookieCraving& other); 106 CookieCraving(CookieCraving&& other); 107 CookieCraving& operator=(const CookieCraving& other); 108 CookieCraving& operator=(CookieCraving&& other); 109 ~CookieCraving() override; 110 111 // Returns whether all CookieCraving fields are consistent, in canonical form, 112 // etc. (Mostly analogous to CanonicalCookie::IsCanonical, except without 113 // checks for access time.) Essentially, if this returns true, then this 114 // CookieCraving instance could have been created by Create(). 115 // Other public methods of this class may not be called if IsValid() is false. 116 bool IsValid() const; 117 118 // Returns whether the given "real" cookie satisfies this CookieCraving, in 119 // the sense that DBSC will consider the required cookie present. 120 // The provided CanonicalCookie must be canonical. 121 bool IsSatisfiedBy(const CanonicalCookie& canonical_cookie) const; 122 123 std::string DebugString() const; 124 125 bool IsEqualForTesting(const CookieCraving& other) const; 126 127 // May return an invalid instance. 128 static CookieCraving CreateUnsafeForTesting( 129 std::string name, 130 std::string domain, 131 std::string path, 132 base::Time creation, 133 bool secure, 134 bool httponly, 135 CookieSameSite same_site, 136 std::optional<CookiePartitionKey> partition_key, 137 CookieSourceScheme source_scheme, 138 int source_port); 139 140 // Returns a protobuf object. May only be called for 141 // a valid CookieCraving object. 142 proto::CookieCraving ToProto() const; 143 144 // Creates a CookieCraving object from a protobuf 145 // object. If the protobuf contents are invalid, 146 // a std::nullopt is returned. 147 static std::optional<CookieCraving> CreateFromProto( 148 const proto::CookieCraving& proto); 149 150 private: 151 CookieCraving(); 152 153 // Prefer Create() over this constructor. This may return non-valid instances. 154 CookieCraving(std::string name, 155 std::string domain, 156 std::string path, 157 base::Time creation, 158 bool secure, 159 bool httponly, 160 CookieSameSite same_site, 161 std::optional<CookiePartitionKey> partition_key, 162 CookieSourceScheme source_scheme, 163 int source_port); 164 }; 165 166 // Outputs a debug string, e.g. for more helpful test failure messages. 167 NET_EXPORT std::ostream& operator<<(std::ostream& os, const CookieCraving& cc); 168 169 } // namespace net::device_bound_sessions 170 171 #endif // NET_DEVICE_BOUND_SESSIONS_COOKIE_CRAVING_H_ 172