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