// Copyright 2024 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef NET_DEVICE_BOUND_SESSIONS_COOKIE_CRAVING_H_ #define NET_DEVICE_BOUND_SESSIONS_COOKIE_CRAVING_H_ #include #include #include "base/time/time.h" #include "net/base/net_export.h" #include "net/cookies/cookie_base.h" #include "net/cookies/cookie_constants.h" #include "net/cookies/cookie_partition_key.h" namespace net { class CanonicalCookie; } namespace net::device_bound_sessions { namespace proto { class CookieCraving; } // This class represents the need for a certain cookie to be present. It is not // a cookie itself, but rather represents a requirement which can be satisfied // by a real cookie (i.e. a CanonicalCookie). Each CookieCraving is specified by // and associated with a DBSC (Device Bound Session Credentials) session. // // In general, CookieCraving behavior is intended to be as close as possible to // CanonicalCookie, especially the inclusion logic, since they need to be // matched up. However, some notable differences include: // // CookieCraving does not have a value field, i.e. they only have a name (and // other attributes). The name can be the empty string. The name of a cookie is // needed to identify it, but the value of a cookie is not relevant to its // inclusion or exclusion, so CookieCraving omits it. // // CookieCraving does not have an expiry date. The expiry date of a // CanonicalCookie often depends upon the creation time (if it is set via // Max-Age), and a DBSC session config is not necessarily created at the same // time as its corresponding Set-Cookie header, so we cannot guarantee that // they'd match. DBSC also does not require a specific expiry date for the // cookies whose presence it guarantees. // // CookieCraving does not implement lax-allow-unsafe behavior (it does not // set a non-zero age threshold for it). The default CanonicalCookie // lax-allow-unsafe behavior is problematic because it can result in two // identical set-cookie lines (set from the same URL) exhibiting different // inclusion results, if they happen to be on opposite sides of the // lax-allow-unsafe age threshold. By not implementing lax-allow-unsafe, // CookieCraving may sometimes be excluded even when a corresponding // CanonicalCookie would be included for being under its lax-allow-unsafe age // threshold. This means that servers deploying DBSC with SameSite-unspecified // cookies SHOULD NOT rely on the presence of SameSite-unspecified cookies // within 2 minutes of their creation time on cross-site POST and other unsafe // request types, as DBSC cannot make any such guarantee. class NET_EXPORT CookieCraving : public CookieBase { public: // Creates a new CookieCraving in the context of `url`, given a `name` and // associated cookie `attributes`. (Note that CookieCravings do not have a // "value".) `url` must be valid. `creation_time` may not be null. May return // nullopt if an attribute value is invalid. If a CookieCraving is returned, // it will satisfy IsValid(). If there is leading or trailing whitespace in // `name`, it will get trimmed. // // `cookie_partition_key` only needs to be present if the attributes contain // the Partitioned attribute. std::nullopt indicates an unpartitioned // CookieCraving will be created. If there is a partition key but the // attributes do not specify Partitioned, the resulting CookieCraving will be // unpartitioned. If the partition_key is nullopt, the CookieCraving will // always be unpartitioned even if the attributes specify Partitioned. // // SameSite and HttpOnly related parameters are not checked here, // so creation of CookieCravings with e.g. SameSite=Strict from a cross-site // context is allowed. Create() also does not check whether `url` has a secure // scheme if attempting to create a Secure cookie. The Secure, SameSite, and // HttpOnly related parameters should be checked when deciding CookieCraving // inclusion for a given request/context. // // In general this is intended to closely mirror CanonicalCookie::Create. // However, there are some simplifying assumptions made*, and metrics are not // (currently) logged so as to not interfere with CanonicalCookie metrics. // There is also no (current) need for a CookieInclusionStatus to be returned. // // * Simplifying assumptions (differing from CanonicalCookie): // - The Domain() member of a CookieCraving is required to be non-empty, // which CanonicalCookie does not require. // - Cookie name prefixes (__Host- and __Secure-) are always checked // case-insensitively, unlike CanonicalCookie which reads a Feature value // to decide whether to check insensitively. // - CanonicalCookie allows non-cryptographic URLs to create a cookie with a // secure source_scheme, if that cookie was Secure, on the basis that that // URL might be trustworthy when checked later. CookieCraving does not // allow this. static std::optional Create( const GURL& url, const std::string& name, const std::string& attributes, base::Time creation_time, std::optional cookie_partition_key); CookieCraving(const CookieCraving& other); CookieCraving(CookieCraving&& other); CookieCraving& operator=(const CookieCraving& other); CookieCraving& operator=(CookieCraving&& other); ~CookieCraving() override; // Returns whether all CookieCraving fields are consistent, in canonical form, // etc. (Mostly analogous to CanonicalCookie::IsCanonical, except without // checks for access time.) Essentially, if this returns true, then this // CookieCraving instance could have been created by Create(). // Other public methods of this class may not be called if IsValid() is false. bool IsValid() const; // Returns whether the given "real" cookie satisfies this CookieCraving, in // the sense that DBSC will consider the required cookie present. // The provided CanonicalCookie must be canonical. bool IsSatisfiedBy(const CanonicalCookie& canonical_cookie) const; std::string DebugString() const; bool IsEqualForTesting(const CookieCraving& other) const; // May return an invalid instance. static CookieCraving CreateUnsafeForTesting( std::string name, std::string domain, std::string path, base::Time creation, bool secure, bool httponly, CookieSameSite same_site, std::optional partition_key, CookieSourceScheme source_scheme, int source_port); // Returns a protobuf object. May only be called for // a valid CookieCraving object. proto::CookieCraving ToProto() const; // Creates a CookieCraving object from a protobuf // object. If the protobuf contents are invalid, // a std::nullopt is returned. static std::optional CreateFromProto( const proto::CookieCraving& proto); private: CookieCraving(); // Prefer Create() over this constructor. This may return non-valid instances. CookieCraving(std::string name, std::string domain, std::string path, base::Time creation, bool secure, bool httponly, CookieSameSite same_site, std::optional partition_key, CookieSourceScheme source_scheme, int source_port); }; // Outputs a debug string, e.g. for more helpful test failure messages. NET_EXPORT std::ostream& operator<<(std::ostream& os, const CookieCraving& cc); } // namespace net::device_bound_sessions #endif // NET_DEVICE_BOUND_SESSIONS_COOKIE_CRAVING_H_