| // 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 <optional> |
| #include <string> |
| |
| #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<CookieCraving> Create( |
| const GURL& url, |
| const std::string& name, |
| const std::string& attributes, |
| base::Time creation_time, |
| std::optional<CookiePartitionKey> 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<CookiePartitionKey> 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<CookieCraving> 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<CookiePartitionKey> 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_ |