1 // Copyright 2021 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_PARTITION_KEY_H_ 6 #define NET_COOKIES_COOKIE_PARTITION_KEY_H_ 7 8 #include <optional> 9 #include <string> 10 11 #include "base/types/expected.h" 12 #include "net/base/cronet_buildflags.h" 13 #include "net/base/features.h" 14 #include "net/base/net_export.h" 15 #include "net/base/network_isolation_key.h" 16 #include "net/base/schemeful_site.h" 17 #include "net/cookies/site_for_cookies.h" 18 #include "url/gurl.h" 19 20 #if !BUILDFLAG(CRONET_BUILD) 21 #include "mojo/public/cpp/bindings/default_construct_tag.h" 22 #endif 23 24 namespace net { 25 26 class NET_EXPORT CookiePartitionKey { 27 public: 28 class NET_EXPORT SerializedCookiePartitionKey { 29 public: 30 const std::string& TopLevelSite() const; 31 bool has_cross_site_ancestor() const; 32 33 private: 34 friend class CookiePartitionKey; 35 // This constructor does not check if the values being serialized are valid. 36 // The caller of this function must ensure that only valid values are passed 37 // to this method. 38 explicit SerializedCookiePartitionKey(const std::string& site, 39 bool has_cross_site_ancestor); 40 41 std::string top_level_site_; 42 bool has_cross_site_ancestor_; 43 }; 44 45 // An enumerated value representing whether any frame in the PartitionKey's 46 // ancestor chain (including the top-level document's site) is cross-site with 47 // the current frame. These values are persisted to disk. Entries should not 48 // be renumbered and numeric values should never be reused. 49 enum class AncestorChainBit { 50 // All frames in the ancestor chain are pairwise same-site. 51 kSameSite = 0, 52 // At least one frame in the ancestor chain is cross-site with 53 // the current frame. 54 kCrossSite = 1, 55 }; 56 57 static AncestorChainBit BoolToAncestorChainBit(bool val); 58 59 CookiePartitionKey() = delete; 60 #if !BUILDFLAG(CRONET_BUILD) 61 explicit CookiePartitionKey(mojo::DefaultConstruct::Tag); 62 #endif 63 CookiePartitionKey(const CookiePartitionKey& other); 64 CookiePartitionKey(CookiePartitionKey&& other); 65 CookiePartitionKey& operator=(const CookiePartitionKey& other); 66 CookiePartitionKey& operator=(CookiePartitionKey&& other); 67 ~CookiePartitionKey(); 68 69 bool operator==(const CookiePartitionKey& other) const; 70 bool operator!=(const CookiePartitionKey& other) const; 71 bool operator<(const CookiePartitionKey& other) const; 72 73 // Methods for serializing and deserializing a partition key to/from a string. 74 // This is currently used for: 75 // - Storing persistent partitioned cookies 76 // - Loading partitioned cookies into Java code 77 // - Sending cookie partition keys as strings in the DevTools protocol 78 // 79 // This function returns true if the partition key is not opaque and if nonce_ 80 // is not present. We do not want to serialize cookies with opaque origins or 81 // nonce in their partition key to disk, because if the browser session ends 82 // we will not be able to attach the saved cookie to any future requests. This 83 // is because opaque origins' nonces are only stored in volatile memory. 84 // 85 // TODO(crbug.com/1225444) Investigate ways to persist partition keys with 86 // opaque origins if a browser session is restored. 87 [[nodiscard]] static base::expected<SerializedCookiePartitionKey, std::string> 88 Serialize(const std::optional<CookiePartitionKey>& in); 89 90 static CookiePartitionKey FromURLForTesting( 91 const GURL& url, 92 AncestorChainBit ancestor_chain_bit = AncestorChainBit::kCrossSite, 93 std::optional<base::UnguessableToken> nonce = std::nullopt) { 94 return CookiePartitionKey(SchemefulSite(url), nonce, ancestor_chain_bit); 95 } 96 97 // Create a partition key from a network isolation key. Partition key is 98 // derived from the key's top-frame site. For scripts, the request_site 99 // is the url of the context running the code. 100 static std::optional<CookiePartitionKey> FromNetworkIsolationKey( 101 const NetworkIsolationKey& network_isolation_key, 102 SiteForCookies site_for_cookies, 103 SchemefulSite request_site); 104 105 // Create a new CookiePartitionKey from the site of an existing 106 // CookiePartitionKey. This should only be used for sites of partition keys 107 // which were already created using Deserialize or FromNetworkIsolationKey. 108 static CookiePartitionKey FromWire( 109 const SchemefulSite& site, 110 AncestorChainBit ancestor_chain_bit, 111 std::optional<base::UnguessableToken> nonce = std::nullopt) { 112 return CookiePartitionKey(site, nonce, ancestor_chain_bit); 113 } 114 115 // Create a new CookiePartitionKey in a script running in a renderer. We do 116 // not trust the renderer to provide us with a cookie partition key, so we let 117 // the renderer use this method to indicate the cookie is partitioned but the 118 // key still needs to be determined. 119 // 120 // When the browser is ingesting cookie partition keys from the renderer, 121 // either the `from_script_` flag should be set or the cookie partition key 122 // should match the browser's. Otherwise the renderer may be compromised. 123 // 124 // TODO(crbug.com/1225444) Consider removing this factory method and 125 // `from_script_` flag when BlinkStorageKey is available in 126 // ServiceWorkerGlobalScope. FromScript()127 static std::optional<CookiePartitionKey> FromScript() { 128 return std::make_optional(CookiePartitionKey(true)); 129 } 130 131 // Create a new CookiePartitionKey from the components of a StorageKey. 132 // Forwards to FromWire, but unlike that method in this one the optional nonce 133 // argument has no default. It also checks that cookie partitioning is enabled 134 // before returning a valid key, which FromWire does not check. 135 [[nodiscard]] static std::optional<CookiePartitionKey> 136 FromStorageKeyComponents(const SchemefulSite& top_level_site, 137 AncestorChainBit ancestor_chain_bit, 138 const std::optional<base::UnguessableToken>& nonce); 139 140 // FromStorage is a factory method which is meant for creating a new 141 // CookiePartitionKey using properties of a previously existing 142 // CookiePartitionKey that was already ingested into storage. This should NOT 143 // be used to create a new CookiePartitionKey that was not previously saved in 144 // storage. 145 [[nodiscard]] static base::expected<std::optional<CookiePartitionKey>, 146 std::string> 147 FromStorage(const std::string& top_level_site, bool has_cross_site_ancestor); 148 149 // This method should be used when the data provided is expected to be 150 // non-null but might be invalid or comes from a potentially untrustworthy 151 // source (such as user-supplied data). 152 // 153 // This reserves FromStorage to handle cases that can result in a null key 154 // (and perfectly validly, like in the case when the top_level_site is empty). 155 [[nodiscard]] static base::expected<CookiePartitionKey, std::string> 156 FromUntrustedInput(const std::string& top_level_site, 157 bool has_cross_site_ancestor); 158 site()159 const SchemefulSite& site() const { return site_; } 160 from_script()161 bool from_script() const { return from_script_; } 162 163 // Returns true if the current partition key can be serialized to a string. 164 // Cookie partition keys whose internal site is opaque cannot be serialized. 165 bool IsSerializeable() const; 166 nonce()167 const std::optional<base::UnguessableToken>& nonce() const { return nonce_; } 168 HasNonce(const std::optional<CookiePartitionKey> & key)169 static bool HasNonce(const std::optional<CookiePartitionKey>& key) { 170 return key && key->nonce(); 171 } 172 IsThirdParty()173 bool IsThirdParty() const { 174 return ancestor_chain_bit_ == AncestorChainBit::kCrossSite; 175 } 176 177 private: 178 explicit CookiePartitionKey(const SchemefulSite& site, 179 std::optional<base::UnguessableToken> nonce, 180 AncestorChainBit ancestor_chain_bit); 181 explicit CookiePartitionKey(bool from_script); 182 183 // This method holds the deserialization logic for validating input from 184 // DeserializeForTesting and FromUntrustedInput which can be used to pass 185 // unserializable top_level_site values. 186 [[nodiscard]] static base::expected<CookiePartitionKey, std::string> 187 DeserializeInternal( 188 const std::string& top_level_site, 189 CookiePartitionKey::AncestorChainBit has_cross_site_ancestor); 190 191 AncestorChainBit MaybeAncestorChainBit() const; 192 193 SchemefulSite site_; 194 bool from_script_ = false; 195 // crbug.com/328043119 remove code associated with 196 // kAncestorChainBitEnabledInPartitionedCookies 197 // when feature is no longer needed. 198 bool ancestor_chain_enabled_ = base::FeatureList::IsEnabled( 199 features::kAncestorChainBitEnabledInPartitionedCookies); 200 201 // Having a nonce is a way to force a transient opaque `CookiePartitionKey` 202 // for non-opaque origins. 203 std::optional<base::UnguessableToken> nonce_; 204 AncestorChainBit ancestor_chain_bit_ = AncestorChainBit::kCrossSite; 205 }; 206 207 // Used so that CookiePartitionKeys can be the arguments of DCHECK_EQ. 208 NET_EXPORT std::ostream& operator<<(std::ostream& os, 209 const CookiePartitionKey& cpk); 210 211 } // namespace net 212 213 #endif // NET_COOKIES_COOKIE_PARTITION_KEY_H_ 214