xref: /aosp_15_r20/external/cronet/net/cookies/cookie_partition_key.h (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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