1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_StoragePrincipalHelper_h
#define mozilla_StoragePrincipalHelper_h
#include <cstdint>
#include "ErrorList.h"
#include "nsStringFwd.h"
/**
* StoragePrincipal
* ~~~~~~~~~~~~~~~~
* StoragePrincipal is the nsIPrincipal to be used to open the cookie jar of a
* resource's origin. Normally, the StoragePrincipal corresponds to the
* resource's origin, but, in some scenarios, it can be different: it has the
* `partitionKey` attribute set to the top-level “site” (i.e., scheme plus
* eTLD+1 of the origin of the top-level document).
*
* Each storage component should always use the StoragePrincipal instead of the
* 'real' one in order to implement the partitioning correctly. See the list of
* the components here: https://privacycg.github.io/storage-partitioning/
*
* On the web, each resource has its own origin (see
* https://html.spec.whatwg.org/multipage/origin.html#concept-origin) and each
* origin has its own cookie jar, containing cookies, storage data, cache and so
* on.
*
* In gecko-world, the origin and its attributes are stored and managed by the
* nsIPrincipal interface. Both a resource's Principal and a resource's
* StoragePrincipal are nsIPrincipal interfaces and, normally, they are the same
* object.
*
* Naming and usage
* ~~~~~~~~~~~~~~~~
*
* StoragePrincipal exposes four types of principals for a resource:
* - Regular Principal:
* A “first-party” principal derived from the origin of the resource. This
* does not have the `partitionKey` origin attribute set.
* - Partitioned Principal:
* The regular principal plus the partitionKey origin attribute set to
* the site of the top-level document (i.e., scheme plus eTLD+1).
* - Storage Access Principal:
* A dynamic principal that changes when a resource receives storage access.
* By default, when storage access is denied, this is equal to the
* Partitioned Principal. When storage access is granted, this is equal to
* the Regular Principal.
* - Foreign Partitioned Principal
* A principal that would be decided according to the fact that if the
* resource is a third party or not. If the resource is in a third-party
* context, this will be the partitioned principal. Otherwise, a regular
* principal will be used. Also, this doesn't like Storage Access Principal
* which changes according to storage access of a resource. Note that this
* is dFPI only; this prinipcal will always return regular principal when
* dFPI is disabled.
*
* Consumers of StoragePrincipal can request the principal type that meets their
* needs. For example, storage that should always be partitioned should choose
* the Partitioned Principal, while storage that should change with storage
* access grants should choose the Storage Access Principal. And the storage
* should be always partiitoned in the third-party context should use the
* Foreign Partitioned Principal.
*
* You can obtain these nsIPrincipal objects:
*
* From a Document:
* - Regular Principal: nsINode::NodePrincipal
* - Storage Access Principal: Document::EffectiveStoragePrincipal
* - Partitioned Principal: Document::PartitionedPrincipal
*
* From a Global object:
* - Regular Principal: nsIScriptObjectPrincipal::GetPrincipal
* - Storage Access Principal:
* nsIScriptObjectPrincipal::GetEffectiveStoragePrincipal
* - Partitioned Principal: nsIScriptObjectPrincipal::PartitionedPrincipal
*
* From a Worker:
* - Regular Principal: WorkerPrivate::GetPrincipal (main-thread)
* - Regular Principal: WorkerPrivate::GetPrincipalInfo (worker thread)
* - Storage Access Principal: WorkerPrivate::GetEffectiveStoragePrincipalInfo
* (worker-thread)
*
* For a nsIChannel, the final principals must be calculated and they can be
* obtained by calling:
* - Regular Principal: nsIScriptSecurityManager::getChannelResultPrincipal
* - Storage Access Principal:
* nsIScriptSecurityManager::getChannelResultStoragePrincipal
* - Partitioned and regular Principal:
* nsIScriptSecurityManager::getChannelResultPrincipals
*
* Each use of nsIPrincipal is unique and it should be reviewed by anti-tracking
* peers. But we can group the use of nsIPrincipal in these categories:
*
* - Network loading: use the Regular Principal
* - Cache, not directly visible by content (network cache, HSTS, image cache,
* etc): Use the Storage Access Principal (in the future we will use the
* Partitioned Principal, but this part is not done yet)
* - Storage APIs or anything that is written on disk (or kept in memory in
* private-browsing): use the Storage Access Principal
* - PostMessage: if in the agent-cluster, use the Regular Principal. Otherwise,
* use the Storage Access Principal
*
* Storage access permission
* ~~~~~~~~~~~~~~~~~~~~~~~~~
*
* When the storage access permission is granted, any of the Storage Access
* Principal getter methods will return the Regular Principal instead of the
* Partitioned Principal, and each storage component should consider the new
* principal only.
*
* The trackers and the 3rd parties (in dFPI) will have access to its
first-party
* cookie jar, escaping from its partitioning.
*
* Storage access permissions can be granted in several ways:
* - The Storage Access API
* (https://developer.mozilla.org/en-US/docs/Web/API/Storage_Access_API)
* - ETP’s heuristics
*
(https://developer.mozilla.org/en-US/docs/Mozilla/Firefox/Privacy/Storage_access_policy#Storage_access_grants)
* - A dFPI-specific login heuristic
* (https://bugzilla.mozilla.org/show_bug.cgi?id=1616585#c12)
*
* There are several ways to receive storage-permission notifications. You can
* use these notifications to re-initialize components, to nullify or enable
them
* to use the “new” effective StoragePrincipal. The list of the notifications
is:
*
* - Add some code in nsGlobalWindowInner::StorageAccessPermissionGranted().
* - WorkerScope::StorageAccessPermissionGranted for Workers.
* - observe the permission changes (not recommended)
*
* Scope of Storage Access
* ~~~~~~~~~~~~~~~~~~~~~~~
*
* Immediately after access is granted, the permission is propagated and
notified
* to any contexts (windows and workers) in the same agent-cluster
* (BrowserContextGroup).
*
* This means that if A.com has 2 iframes with B.com, and one of the 2 Bs
obtains
* the storage access, the other B will be notified too. Other B.com, 3rd
parties
* in other agent clusters will not obtain the storage permission.
*
* When the page is reloaded or is loaded for the first time, if it contains
* B.com, and B.com has received the storage permission for the same first-party
* in a previous loading, B.com will have the storage access permission granted
* immediately.
*
* Cookies, LocalStorage, indexedDB
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* When granting storage permission, several storage and channel API getters and
* constructors will start exposing first-party cookie jar objects
(localStorage,
* BroadcastChannel, etc).
*
* There is a side effect of this change: If a tracker has a reference to these
* objects pre-storage permission granting, it will be able to interact with the
* partitioned and the non-partitioned cookie jar at the same time. Note that
* similar synchronization can be done server-side too. Because of this, we
don’t
* think that privacy-wise, this is an issue.
*
* localStorage supports StoragePrincipal, and will be switched after storage
* access is granted. Trackers listed in the pref
* privacy.restrict3rdpartystorage.partitionedHosts will use another special
* partitioned session-only storage called PartitionedLocalStorage.
*
* sessionStorage is not covered by StoragePrincipal, but is double-keyed using
* the top-level site when dFPI is active
* (https://bugzilla.mozilla.org/show_bug.cgi?id=1629707).
*
* SharedWorkers and BroadcastChannels
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* SharedWorker and BroadcastChannel instances latch the effective storage
* principal at the moment of their creation. Existing bindings to the
* partitioned storage principal will continue to exist and operate even as it
* becomes possible to create bindings associated with the Regular Principal.
* This makes it possible for such globals to bi-directionally bridge
information
* between partitioned and non-partitioned principals.
*
* This is true until the page is reloaded. After the reload, the partitioned
* cookie jar will no longer be accessible.
*
* We are planning to clear the partitioned site-data as soon as the page is
* reloaded or dismissed (not done yet - bug 1628313).
*
* {Dedicated,Shared,Service}Workers
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* The storage access permission propagation happens with a ControlRunnable.
This
* could impact the use of sync event-loops. Take a reference of the principal
* you want to use because it can change!
*
* ServiceWorkers are currently disabled for partitioned contexts.
*
* Client API uses the regular nsIPrincipal always because there is not a direct
* connection between this API and the cookie jar. If we want to support
* ServiceWorkers in partitioned context, this part must be revisited.
*/
class nsIChannel;
class nsICookieJarSettings;
class nsIDocShell;
class nsILoadGroup;
class nsIPrincipal;
class nsIURI;
class nsPIDOMWindowInner;
namespace mozilla {
namespace dom {
class Document;
class WorkerPrivate;
} // namespace dom
namespace ipc {
class PrincipalInfo;
}
class OriginAttributes;
class StoragePrincipalHelper final {
public:
static nsresult Create(nsIChannel* aChannel, nsIPrincipal* aPrincipal,
bool aForceIsolation,
nsIPrincipal** aStoragePrincipal);
static nsresult CreatePartitionedPrincipalForServiceWorker(
nsIPrincipal* aPrincipal, nsICookieJarSettings* aCookieJarSettings,
nsIPrincipal** aPartitionedPrincipal);
static nsresult PrepareEffectiveStoragePrincipalOriginAttributes(
nsIChannel* aChannel, OriginAttributes& aOriginAttributes);
// A helper function to verify storage principal info with the principal info.
static bool VerifyValidStoragePrincipalInfoForPrincipalInfo(
const mozilla::ipc::PrincipalInfo& aStoragePrincipalInfo,
const mozilla::ipc::PrincipalInfo& aPrincipalInfo);
// A helper function to verify client principal info with the principal info.
//
// Note that the client principal refers the principal of the client, which is
// supposed to be the foreign partitioned principal.
static bool VerifyValidClientPrincipalInfoForPrincipalInfo(
const mozilla::ipc::PrincipalInfo& aClientPrincipalInfo,
const mozilla::ipc::PrincipalInfo& aPrincipalInfo);
enum PrincipalType {
// This is the first-party principal.
eRegularPrincipal,
// This is a dynamic principal based on the current state of the origin. If
// the origin has the storage permission granted, effective storagePrincipal
// will be the regular principal, otherwise, the partitioned Principal
// will be used.
eStorageAccessPrincipal,
// This is the first-party principal, plus, First-party isolation attribute
// set.
ePartitionedPrincipal,
// This principal returns different results based on whether its associated
// channel/window is in a third-party context. While in a third-party
// context, it returns the partitioned principal; otherwise, it returns the
// regular principal.
//
// Note that this principal is not a dynamic principal like
// `eStorageAccessPrincipal`, which changes depending on whether the storage
// access permission is granted. This principal doesn't take the storage
// access permission into consideration. Also, this principle is used in
// dFPI only, meaning that it always returns the regular principal when dFP
// Is disabled.
eForeignPartitionedPrincipal,
};
/**
* Extract the principal from the channel/document according to the given
* principal type.
*/
static nsresult GetPrincipal(nsIChannel* aChannel,
PrincipalType aPrincipalType,
nsIPrincipal** aPrincipal);
static nsresult GetPrincipal(nsPIDOMWindowInner* aWindow,
PrincipalType aPrincipalType,
nsIPrincipal** aPrincipal);
// Check if we need to use the partitioned principal for the service worker of
// the given docShell. Please do not use this API unless you cannot get the
// foreign partitioned principal, e.g. creating the inital about:blank page.
static bool ShouldUsePartitionPrincipalForServiceWorker(
nsIDocShell* aDocShell);
static bool ShouldUsePartitionPrincipalForServiceWorker(
dom::WorkerPrivate* aWorkerPrivate);
/**
* Extract the right OriginAttributes from the channel's triggering
* principal.
*/
static bool GetOriginAttributes(nsIChannel* aChannel,
OriginAttributes& aAttributes,
PrincipalType aPrincipalType);
static bool GetRegularPrincipalOriginAttributes(
dom::Document* aDocument, OriginAttributes& aAttributes);
static bool GetRegularPrincipalOriginAttributes(
nsILoadGroup* aLoadGroup, OriginAttributes& aAttributes);
// These methods return the correct originAttributes to be used for network
// state components (HSTS, network cache, image-cache, and so on).
static bool GetOriginAttributesForNetworkState(nsIChannel* aChannel,
OriginAttributes& aAttributes);
static void GetOriginAttributesForNetworkState(dom::Document* aDocument,
OriginAttributes& aAttributes);
static void UpdateOriginAttributesForNetworkState(
nsIURI* aFirstPartyURI, OriginAttributes& aAttributes);
// For HSTS we want to force 'HTTP' in the partition key.
static bool GetOriginAttributesForHSTS(nsIChannel* aChannel,
OriginAttributes& aAttributes);
// Like the function above, this function forces `HTTPS` in the partition key.
// The OA created by this function is mainly used in DNS cache. The spec
// specifies that the presence of HTTPS RR for an origin also indicates that
// all HTTP resources are available over HTTPS, so we use this function to
// ensure that all HTTPS RRs in DNS cache are accessed by HTTPS requests only.
static bool GetOriginAttributesForHTTPSRR(nsIChannel* aChannel,
OriginAttributes& aAttributes);
// Get the origin attributes from a PrincipalInfo
static bool GetOriginAttributes(
const mozilla::ipc::PrincipalInfo& aPrincipalInfo,
OriginAttributes& aAttributes);
static bool PartitionKeyHasBaseDomain(const nsAString& aPartitionKey,
const nsACString& aBaseDomain);
static bool PartitionKeyHasBaseDomain(const nsAString& aPartitionKey,
const nsAString& aBaseDomain);
// Partition keys can have the same-site bit added or removed from them.
// "(https,foo.com)", false -> "(https,foo.com)"
// "(https,foo.com,f)", false -> "(https,foo.com)"
// "(https,foo.com,f)", true -> "(https,foo.com,f)"
// "(https,foo.com)", true -> "(https,foo.com,f)"
static void UpdatePartitionKeyWithForeignAncestorBit(
nsAString& aKey, bool aForeignByAncestorContext);
};
} // namespace mozilla
#endif // mozilla_StoragePrincipalHelper_h
|