// Copyright 2021 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_COOKIES_COOKIE_PARTITION_KEY_H_ #define NET_COOKIES_COOKIE_PARTITION_KEY_H_ #include #include #include "base/types/expected.h" #include "net/base/cronet_buildflags.h" #include "net/base/features.h" #include "net/base/net_export.h" #include "net/base/network_isolation_key.h" #include "net/base/schemeful_site.h" #include "net/cookies/site_for_cookies.h" #include "url/gurl.h" #if !BUILDFLAG(CRONET_BUILD) #include "mojo/public/cpp/bindings/default_construct_tag.h" #endif namespace net { class NET_EXPORT CookiePartitionKey { public: class NET_EXPORT SerializedCookiePartitionKey { public: const std::string& TopLevelSite() const; bool has_cross_site_ancestor() const; private: friend class CookiePartitionKey; // This constructor does not check if the values being serialized are valid. // The caller of this function must ensure that only valid values are passed // to this method. explicit SerializedCookiePartitionKey(const std::string& site, bool has_cross_site_ancestor); std::string top_level_site_; bool has_cross_site_ancestor_; }; // An enumerated value representing whether any frame in the PartitionKey's // ancestor chain (including the top-level document's site) is cross-site with // the current frame. These values are persisted to disk. Entries should not // be renumbered and numeric values should never be reused. enum class AncestorChainBit { // All frames in the ancestor chain are pairwise same-site. kSameSite = 0, // At least one frame in the ancestor chain is cross-site with // the current frame. kCrossSite = 1, }; static AncestorChainBit BoolToAncestorChainBit(bool val); CookiePartitionKey() = delete; #if !BUILDFLAG(CRONET_BUILD) explicit CookiePartitionKey(mojo::DefaultConstruct::Tag); #endif CookiePartitionKey(const CookiePartitionKey& other); CookiePartitionKey(CookiePartitionKey&& other); CookiePartitionKey& operator=(const CookiePartitionKey& other); CookiePartitionKey& operator=(CookiePartitionKey&& other); ~CookiePartitionKey(); bool operator==(const CookiePartitionKey& other) const; bool operator!=(const CookiePartitionKey& other) const; bool operator<(const CookiePartitionKey& other) const; // Methods for serializing and deserializing a partition key to/from a string. // This is currently used for: // - Storing persistent partitioned cookies // - Loading partitioned cookies into Java code // - Sending cookie partition keys as strings in the DevTools protocol // // This function returns true if the partition key is not opaque and if nonce_ // is not present. We do not want to serialize cookies with opaque origins or // nonce in their partition key to disk, because if the browser session ends // we will not be able to attach the saved cookie to any future requests. This // is because opaque origins' nonces are only stored in volatile memory. // // TODO(crbug.com/1225444) Investigate ways to persist partition keys with // opaque origins if a browser session is restored. [[nodiscard]] static base::expected Serialize(const std::optional& in); static CookiePartitionKey FromURLForTesting( const GURL& url, AncestorChainBit ancestor_chain_bit = AncestorChainBit::kCrossSite, std::optional nonce = std::nullopt) { return CookiePartitionKey(SchemefulSite(url), nonce, ancestor_chain_bit); } // Create a partition key from a network isolation key. Partition key is // derived from the key's top-frame site. For scripts, the request_site // is the url of the context running the code. static std::optional FromNetworkIsolationKey( const NetworkIsolationKey& network_isolation_key, SiteForCookies site_for_cookies, SchemefulSite request_site); // Create a new CookiePartitionKey from the site of an existing // CookiePartitionKey. This should only be used for sites of partition keys // which were already created using Deserialize or FromNetworkIsolationKey. static CookiePartitionKey FromWire( const SchemefulSite& site, AncestorChainBit ancestor_chain_bit, std::optional nonce = std::nullopt) { return CookiePartitionKey(site, nonce, ancestor_chain_bit); } // Create a new CookiePartitionKey in a script running in a renderer. We do // not trust the renderer to provide us with a cookie partition key, so we let // the renderer use this method to indicate the cookie is partitioned but the // key still needs to be determined. // // When the browser is ingesting cookie partition keys from the renderer, // either the `from_script_` flag should be set or the cookie partition key // should match the browser's. Otherwise the renderer may be compromised. // // TODO(crbug.com/1225444) Consider removing this factory method and // `from_script_` flag when BlinkStorageKey is available in // ServiceWorkerGlobalScope. static std::optional FromScript() { return std::make_optional(CookiePartitionKey(true)); } // Create a new CookiePartitionKey from the components of a StorageKey. // Forwards to FromWire, but unlike that method in this one the optional nonce // argument has no default. It also checks that cookie partitioning is enabled // before returning a valid key, which FromWire does not check. [[nodiscard]] static std::optional FromStorageKeyComponents(const SchemefulSite& top_level_site, AncestorChainBit ancestor_chain_bit, const std::optional& nonce); // FromStorage is a factory method which is meant for creating a new // CookiePartitionKey using properties of a previously existing // CookiePartitionKey that was already ingested into storage. This should NOT // be used to create a new CookiePartitionKey that was not previously saved in // storage. [[nodiscard]] static base::expected, std::string> FromStorage(const std::string& top_level_site, bool has_cross_site_ancestor); // This method should be used when the data provided is expected to be // non-null but might be invalid or comes from a potentially untrustworthy // source (such as user-supplied data). // // This reserves FromStorage to handle cases that can result in a null key // (and perfectly validly, like in the case when the top_level_site is empty). [[nodiscard]] static base::expected FromUntrustedInput(const std::string& top_level_site, bool has_cross_site_ancestor); const SchemefulSite& site() const { return site_; } bool from_script() const { return from_script_; } // Returns true if the current partition key can be serialized to a string. // Cookie partition keys whose internal site is opaque cannot be serialized. bool IsSerializeable() const; const std::optional& nonce() const { return nonce_; } static bool HasNonce(const std::optional& key) { return key && key->nonce(); } bool IsThirdParty() const { return ancestor_chain_bit_ == AncestorChainBit::kCrossSite; } private: explicit CookiePartitionKey(const SchemefulSite& site, std::optional nonce, AncestorChainBit ancestor_chain_bit); explicit CookiePartitionKey(bool from_script); // This method holds the deserialization logic for validating input from // DeserializeForTesting and FromUntrustedInput which can be used to pass // unserializable top_level_site values. [[nodiscard]] static base::expected DeserializeInternal( const std::string& top_level_site, CookiePartitionKey::AncestorChainBit has_cross_site_ancestor); AncestorChainBit MaybeAncestorChainBit() const; SchemefulSite site_; bool from_script_ = false; // crbug.com/328043119 remove code associated with // kAncestorChainBitEnabledInPartitionedCookies // when feature is no longer needed. bool ancestor_chain_enabled_ = base::FeatureList::IsEnabled( features::kAncestorChainBitEnabledInPartitionedCookies); // Having a nonce is a way to force a transient opaque `CookiePartitionKey` // for non-opaque origins. std::optional nonce_; AncestorChainBit ancestor_chain_bit_ = AncestorChainBit::kCrossSite; }; // Used so that CookiePartitionKeys can be the arguments of DCHECK_EQ. NET_EXPORT std::ostream& operator<<(std::ostream& os, const CookiePartitionKey& cpk); } // namespace net #endif // NET_COOKIES_COOKIE_PARTITION_KEY_H_