Source code

Revision control

Copy as Markdown

Other Tools

/* -*- 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
*
* On the web, each resource has its own origin (see
* 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
* - ETP’s heuristics
*
* - A dFPI-specific login heuristic
*
* 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
*
* 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