summaryrefslogtreecommitdiffstats
path: root/toolkit/components/antitracking/StoragePrincipalHelper.h
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/components/antitracking/StoragePrincipalHelper.h')
-rw-r--r--toolkit/components/antitracking/StoragePrincipalHelper.h358
1 files changed, 358 insertions, 0 deletions
diff --git a/toolkit/components/antitracking/StoragePrincipalHelper.h b/toolkit/components/antitracking/StoragePrincipalHelper.h
new file mode 100644
index 0000000000..f813417eb6
--- /dev/null
+++ b/toolkit/components/antitracking/StoragePrincipalHelper.h
@@ -0,0 +1,358 @@
+/* -*- 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);
+};
+
+} // namespace mozilla
+
+#endif // mozilla_StoragePrincipalHelper_h