├── .github
└── workflows
│ └── build.yml
├── .gitignore
├── .pr-preview.json
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE.md
├── Makefile
├── README.md
├── adjacent_work
├── TurtledoveSequenceDiagram.png
└── TurtledoveWorklets.md
├── alternate_usecases_analysis
└── PrompltessUnpartitionedStorageAccess.md
├── explainer
├── README.md
├── TAG_Security_Privacy_Questionnaire.md
├── fenced_frame_config.md
├── fenced_frame_config_context.md
├── fenced_frames_with_local_unpartitioned_data_access.md
├── generic_payment_button.png
├── integration_with_web_platform.md
├── interaction_with_content_security_policy.md
├── network_side_channel.md
├── opaque_ads_use_cases.md
├── permission_document_policies.md
├── permissions_policy_for_API_backed_fenced_frames.md
├── personalized_payment_button.png
├── process_isolation.md
├── storage_cookies_network_state.md
└── use_cases.md
├── meetings
└── TPAC2024Notes.md
├── spec.bs
└── w3c.json
/.github/workflows/build.yml:
--------------------------------------------------------------------------------
1 | name: Build
2 | on:
3 | pull_request: {}
4 | push:
5 | branches:
6 | - master
7 | jobs:
8 | build:
9 | name: Build
10 | runs-on: ubuntu-latest
11 | steps:
12 | - uses: actions/checkout@v3
13 | - uses: w3c/spec-prod@v2
14 | with:
15 | TOOLCHAIN: bikeshed
16 | SOURCE: spec.bs
17 | DESTINATION: index.html
18 | GH_PAGES_BRANCH: gh-pages
19 | BUILD_FAIL_ON: warning
20 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | spec.html
2 | out
3 |
--------------------------------------------------------------------------------
/.pr-preview.json:
--------------------------------------------------------------------------------
1 | {
2 | "src_file": "spec.bs",
3 | "type": "bikeshed"
4 | }
5 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Code of Conduct
2 |
3 | All documentation, code and communication under this repository are covered by the [W3C Code of Ethics and Professional Conduct](https://www.w3.org/Consortium/cepc/).
4 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Web Platform Incubator Community Group
2 |
3 | This repository is being used for work in the W3C Web Platform Incubator Community Group, governed by the [W3C Community License
4 | Agreement (CLA)](http://www.w3.org/community/about/agreements/cla/). To make substantive contributions,
5 | you must join the CG.
6 |
7 | If you are not the sole contributor to a contribution (pull request), please identify all
8 | contributors in the pull request comment.
9 |
10 | To add a contributor (other than yourself, that's automatic), mark them one per line as follows:
11 |
12 | ```
13 | +@github_username
14 | ```
15 |
16 | If you added a contributor by mistake, you can remove them in a comment with:
17 |
18 | ```
19 | -@github_username
20 | ```
21 |
22 | If you are making a pull request on behalf of someone else but you had no part in designing the
23 | feature, you can remove yourself with the above syntax.
24 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | All Reports in this Repository are licensed by Contributors under the [W3C Software and Document License](http://www.w3.org/Consortium/Legal/2015/copyright-software-and-document).
2 |
3 | Contributions to Specifications are made under the [W3C CLA](https://www.w3.org/community/about/agreements/cla/).
4 |
5 | Contributions to Test Suites are made under the [W3C 3-clause BSD License](https://www.w3.org/Consortium/Legal/2008/03-bsd-license.html).
6 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | SHELL=/bin/bash
2 |
3 | local: spec.bs
4 | bikeshed --die-on=warning spec spec.bs spec.html
5 |
6 | watch: spec.bs
7 | bikeshed watch --die-on=warning spec.bs spec.html
8 |
9 | spec.html: spec.bs
10 | @ (HTTP_STATUS=$$(curl https://api.csswg.org/bikeshed/ \
11 | --output spec.html \
12 | --write-out "%{http_code}" \
13 | --header "Accept: text/plain, text/html" \
14 | -F die-on=warning \
15 | -F file=@spec.bs) && \
16 | [[ "$$HTTP_STATUS" -eq "200" ]]) || ( \
17 | echo ""; cat spec.html; echo ""; \
18 | rm -f spec.html; \
19 | exit 22 \
20 | );
21 |
22 | remote: spec.html
23 |
24 | clean:
25 | rm spec.html
26 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Fenced Frames
2 |
3 | This is the repository for Fenced Frames.
4 |
5 | See the [draft specification](https://wicg.github.io/fenced-frame).
6 |
7 | The explainer is organized as follows:
8 |
9 | * What is a fenced frame: the concept, HTML element, security and privacy considerations
10 | * [explainer/README](https://github.com/WICG/fenced-frame/tree/master/explainer)
11 |
12 | * What are the problems being solved/ different modes of fenced frames?
13 | * [Modes/Use cases of fenced frames](https://github.com/WICG/fenced-frame/blob/master/explainer/use_cases.md)
14 |
15 | * How fenced frames integrates with the web platform. This is a growing list of things and will be added to the documentation
16 | * [Integration with the web platform](https://github.com/WICG/fenced-frame/blob/master/explainer/integration_with_web_platform.md)
17 |
18 |
19 |
--------------------------------------------------------------------------------
/adjacent_work/TurtledoveSequenceDiagram.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WICG/fenced-frame/0fd535135e33ec42dc2cdb1b5fd4e0923d3da565/adjacent_work/TurtledoveSequenceDiagram.png
--------------------------------------------------------------------------------
/adjacent_work/TurtledoveWorklets.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | **Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)*
4 |
5 | - [Turtledove Worklets](#turtledove-worklets)
6 | - [Motivation and privacy threat model](#motivation-and-privacy-threat-model)
7 | - [Design](#design)
8 | - [Isolation characteristics](#isolation-characteristics)
9 | - [JS Execution context and thread/process](#js-execution-context-and-threadprocess)
10 | - [Worklet per functionality per origin](#worklet-per-functionality-per-origin)
11 | - [Performance and lifetime considerations](#performance-and-lifetime-considerations)
12 | - [Isolation from extensions](#isolation-from-extensions)
13 | - [Types of Worklets](#types-of-worklets)
14 | - [Privacy and Security considerations](#privacy-and-security-considerations)
15 |
16 |
17 |
18 | # Turtledove Worklets
19 |
20 | (Not to be confused with Worklets Web API as these Javascript contexts are not web-platform visible but are created by the browser.)
21 |
22 | ## Motivation and privacy threat model
23 |
24 | [TURTLEDOVE](https://github.com/WICG/turtledove) requires the browser to run custom javascripts, written by the various actors (like an ad-tech or publisher or advertiser) that participate in the [on-device auction](https://github.com/WICG/turtledove#on-device-auction) of ads. The on-device auction requires signals from the publisher page as well as looking at the interest groups the user is part of and makes the winning ad decision based on both of these.
25 |
26 | Since this environment has both the user’s unpartitioned (available across all sites) information - the interest groups, as well as the publisher page’s information, it can join these two. If this joined information is persisted or sent to a server, it can lead to the advertiser network knowing the user’s browsing history or the user’s identity on the publisher page. It can also lead to the publisher page knowing the interest group that the user belongs to. These are contrary to the [privacy guarantees](https://github.com/WICG/turtledove/blob/main/Original-TURTLEDOVE.md#introduction) that TURTLEDOVE is designed to enforce.
27 |
28 | To mitigate these privacy threats, this JS environment needs to be isolated and cannot access the network, storage or exfiltrate any information to the publisher page.
29 |
30 |
31 | ## Design
32 |
33 | The TURTLEDOVE worklet is an isolated Javascript execution environment that acts as a pure function and does not exfiltrate any data it has access to via its inputs or the results of invoking browser APIs.
34 |
35 | Let’s dive deeper into how this environment will be used by looking at the end-to-end workflow.
36 |
37 |
38 |
39 | * **Creation**: The TURTLEDOVE worklets are created from within the TURTLEDOVE APIs and not by the webpage (unlike other web worklets e.g. animation worklet etc.), therefore this explainer does not introduce any web facing API for its creation. For example, the TURTLEDOVE API is the one that is invoked by the JS running in the publisher page environment, and that in turn creates the worklet to run various pieces of ad-tech-written code in. For more details on the TURTLEDOVE APIs, see [here](https://github.com/WICG/turtledove/blob/master/FLEDGE.md).
40 | * **Allowed APIs**: The worklets will be allowed to execute only a minimal set of APIs (list of which needs to be created). The reporting worklet will access the [aggregate reporting API](https://github.com/csharrison/aggregate-reporting-api).
41 | * **Network access**: Since the worklet has access to the publisher page information as well as user’s cross-site data (e.g. ad interest groups), the worklet can join them, thus enabling user’s browsing history tracking by the ad-tech network. To mitigate the exfiltration of this joined data the worklet is not allowed network access.
42 | * **Storage access**: For similar reasons, the worklet should also not be able to persist information e.g. in local storage etc. The worklet should not have access to storage.
43 | * **Communication with the page**: Since the worklet has access to user’s interest groups, it cannot share that with the embedding page and thus communication to the embedding page is not allowed.
44 | * **Restricted Outputs**: The winning ad output from the worklet needs to be further worked upon by the TURTLEDOVE API for the following aspects:
45 | * Confirm if the output is k-anonymous i.e. the same ad is seen by ‘n’ other browsers. If not, re-run the auction logic.
46 | * Map the winning ad url to an opaque url before passing it back to the publisher page.
47 |
48 | ## Isolation characteristics
49 |
50 | This section goes into the isolation characteristics of the worklets.
51 |
52 |
53 | ### JS Execution context and thread/process
54 |
55 | Each worklet will be a separate JS execution context (e.g. in Chromium, a separate [V8 context](https://chromium.googlesource.com/chromium/src/+/master/third_party/blink/renderer/bindings/core/v8/V8BindingDesign.md#Context)). A TURTLEDOVE worklet needs to execute off the main thread to make sure the bidding and auction logic do not slow down the main thread. The design will go into more details about the process isolation of the worklets, but a high level overview is that these worklets will benefit by being placed in separate processes than the publisher page so that other compromised renderer processes are not able to alter the sensitive data e.g. bidding price, interest group data, present in these worklets. This is aligned with the threat model for Chromium’s [site isolation](https://www.chromium.org/Home/chromium-security/site-isolation). The design will also discuss protection of the worklets from spectre attacks.
56 |
57 |
58 | ### Worklet per functionality per origin
59 |
60 | TURTLEDOVE requires multiple types of worklets for different functionality as mentioned in the section [“Types of Worklets”](#types-of-worklets). An ideal separation between these different worklets is to run each script provided by the different entities like DSP, SSP etc. for different functionalities like bidding, auction etc., in a separate worklet. This will make it easier to restrict the input information flowing into each of these worklets and to enforce privacy guarantees. Examples of how this isolation will help are:
61 |
62 |
63 |
64 | * Bidding logic from one buyer/origin should not impact the bidding logic from another buyer.
65 | * Bidding logic cannot impact the auction logic by setting global variables in the JS context.
66 |
67 | #### Performance and lifetime considerations
68 |
69 | The TURTLEDOVE worklets from one origin being isolated from those of other origins is a hard privacy and security requirement. Similarly, these worklets are scoped to a document and cannot be reused if the top-level page navigates since worklets have the publisher page info and that cannot be leaked across navigations.
70 |
71 | From a performance standpoint, considering that creating multiple worklets/JS contexts for a document is going to be some amount of performance/memory cost and to keep that cost down, browsers might want to create one worklet per origin for one mode (buyer/seller) and use it across various ads on the page instead of creating them separately for each interest group for each ad. The decision to re-use a worklet across ads will not be deterministic and depends on the browser. On the other hand, if there is just a single context for all ad-slots for one DSP, it may mean that the auctions of the various ad slots are sequentially executed leading to ad loading delays. There needs to be a more detailed design of how this can be optimized.
72 |
73 |
74 | ### Isolation from extensions
75 |
76 | Although bidding/auction scripts that are executed inside the worklets are free to be blocked by extensions installed by the user, there is a risk to the integrity of the ads auction if the scripts are allowed to be modified by a potentially malicious extension or if a malicious extension is able to execute in the script’s context. This is because TURTLEDOVE moves sensitive server side bidding and auction logic to the browser.
77 |
78 | Note that the isolation from extensions mentioned in this section is scoped to modification by extensions and not blocking of content by extensions which will be allowed to proceed as for regular network requests.
79 |
80 | The following safeguards help to achieve that goal within the worklet:
81 |
82 |
83 |
84 | * As discussed more in the design section below, a separate V8 context guarantees that an extension cannot directly execute in that context.
85 | * Since the worklet does not have access to the DOM, it guarantees that the extension cannot mutate the DOM to affect the worklet.
86 | * Since the worklet does not access the network, it will not be affected by APIs like WebRequest.
87 |
88 | TURTLEDOVE/FLEDGE do have other network accessing parts like fetching the scripts to run in the worklets and [trusted server interaction](https://github.com/WICG/turtledove/blob/master/FLEDGE.md#31-fetching-real-time-data-from-a-trusted-server) for real-time signals and blocking of those network requests by extensions will be allowed. However, it needs more detailed design to understand if/whether those will need to be restricted modification by extensions or would they instead be verified server-side to confirm that an unmodified script was executed.
89 |
90 | ## Types of Worklets
91 |
92 | As discussed in [FLEDGE](https://github.com/WICG/turtledove/blob/master/FLEDGE.md), TURTLEDOVE will require worklets for its various functionalities, including:
93 |
94 |
95 |
96 | * **Bidding**: The worklet responsible for executing the bidding script. For more details about the responsibilities of this worklet, see [here](https://github.com/WICG/turtledove/blob/master/FLEDGE.md#32-on-device-bidding).
97 | * **Auction**: The worklet responsible for executing the auction script. For more details about the responsibilities of this worklet, see [here](https://github.com/WICG/turtledove/blob/master/FLEDGE.md#23-scoring-bids).
98 | * **Reporting**: The reporting worklet will be responsible for getting inputs from various parts of the page as given below and creating an event-level report (for FLEDGE as MVP) or an [aggregated report](https://github.com/csharrison/aggregate-reporting-api) (long-term) based on those inputs. These reports can be used for various uses like impression counting, conversion measurement, determining fraud or abuse, diagnostics/error reporting etc. Detailed design for these reports needs to be done.
99 | * Publisher page provides inputs like viewability events for the fenced frame, ancestor origins etc.
100 | * Ad rendered in the fenced frame provides inputs like click time etc.
101 | * Bidding/Auction worklet provides inputs like winning ad price and diagnostics like JS errors, determining if codepaths are still live, latency measurement, etc.
102 |
103 |
104 | ## Privacy and Security considerations
105 |
106 | Privacy and Security considerations have been detailed in the sections on [“Design”](#design) and [“Isolation characteristics”](#isolation-characteristics).
107 |
108 | In addition to those, since TURTLEDOVE is sensitive in nature and requires protections against MitM-type attacks like modifying the bidding script/values on the network, these can only be fetched via HTTPS. This is in line with the guidance [here](https://www.chromium.org/Home/chromium-security/prefer-secure-origins-for-powerful-new-features).
109 |
--------------------------------------------------------------------------------
/alternate_usecases_analysis/PrompltessUnpartitionedStorageAccess.md:
--------------------------------------------------------------------------------
1 | # Fenced frames and promptless unpartitioned storage access
2 |
3 |
4 |
5 | ## Introduction
6 |
7 | This document goes into the details of promptless unpartitioned storage access as a potential use case of fenced frames ([privacyCG discussion](https://github.com/privacycg/storage-access/issues/41)). For fenced frames concept and design please see the [explainer](https://github.com/WICG/fenced-frame).
8 |
9 |
10 | ## Unpartitioned storage access
11 |
12 | The fenced frame does not have storage access by default. We want the fenced frame to have access to unpartitioned storage, if needed.
13 |
14 | The storage access will be gated behind a user gesture and the states of a fenced frame would be:
15 |
16 |
17 |
18 | 1. Start
19 | * No storage access
20 | 2. On user activation and requesting storage access
21 | * Read/write unpartitioned state access granted
22 |
23 | There are a number of use cases for unpartitioned storage access. These include embedded media playing and enqueueing, document viewing and editing, social widgets, and article comments. requestStorageAccess within the fenced frame can be used to fulfill these use cases.
24 |
25 | Although many of these use cases could be handled with a combination of user identification and server-side storage, the common way to identify users today is from their storage (cookies). Also, any offline use cases (such as offline docs) would require client-side storage.
26 |
27 |
28 | ### Challenges
29 |
30 | requestStorageAccess is used to provide access to unpartitioned storage. When invoked within fenced frames, the goal is to not show a permission prompt, thanks to the communication isolation of a fenced frame. However, that is dependent on mitigating challenges like link decoration, network timing etc. as discussed in the thread [here](https://github.com/privacycg/storage-access/issues/41#issuecomment-673057755).
31 |
32 |
33 | ## Alternative state machine considered
34 |
35 | An alternative that was considered was to provide read-only access to the storage if requested before user activation which can later be converted to read-write storage. Before user activation, unpartitioned storage is only available in a read-only mode to make sure that any bits of information that may have been passed along to the fenced frame e.g. using frame size, is not persisted. But once the read-only data is provided, network access is disabled until user activation. The state machine then becomes.
36 |
37 |
38 |
39 | 1. Start
40 | * Full network access, no storage access
41 | 2. On requesting read-only storage access without user activation
42 | * No network access, read-only unpartitioned state (if requested)
43 | 3. On user activation
44 | * Full network access, read/write unpartitioned state (if requested)
45 |
46 | Downsides
47 |
48 |
49 |
50 | * Increased complexity since allowing read-only access in certain storage mechanisms like IndexedDB, can lead to side channels, for example holding a transaction open for a read could be inferred by observing blocked read-write operations in another context.
51 |
--------------------------------------------------------------------------------
/explainer/README.md:
--------------------------------------------------------------------------------
1 | **Table of Contents**
2 |
3 | - [Explainer - Fenced Frames](#explainer---fenced-frames)
4 | - [Authors](#authors)
5 | - [Introduction](#introduction)
6 | - [Goals](#goals)
7 | - [Design](#design)
8 | - [Fenced frame API](#fenced-frame-api)
9 | - [New element type - a top-level browsing context](#new-element-type---a-top-level-browsing-context)
10 | - [Example usage](#example-usage)
11 | - [Benefits over nested browsing context](#benefits-over-nested-browsing-context)
12 | - [Downsides of a new element](#downsides-of-a-new-element)
13 | - [Fenced frame tree](#fenced-frame-tree)
14 | - [Information channel between fenced frame and other frames](#information-channel-between-fenced-frame-and-other-frames)
15 | - [Security considerations](#security-considerations)
16 | - [Privacy considerations](#privacy-considerations)
17 | - [Ongoing technical constraints](#ongoing-technical-constraints)
18 | - [Parallels with Cross-site portals](#parallels-with-cross-site-portals)
19 | - [API alternatives considered](#api-alternatives-considered)
20 | - [Using iframe with document policy](#using-iframe-with-document-policy)
21 | - [Using a new iframe attribute](#using-a-new-iframe-attribute)
22 | - [Using Feature policy/Permission policy](#using-feature-policypermission-policy)
23 |
24 |
25 |
26 | # Explainer - Fenced Frames
27 |
28 | ## Authors
29 | * Shivani Sharma
30 | * Josh Karlin
31 |
32 | ## Introduction
33 | In a web that has its cookies and storage partitioned by top-frame site, there are occasions (such as [Interest group based advertising](https://github.com/WICG/turtledove) or [Conversion Lift Measurements](https://github.com/w3c/web-advertising/blob/master/support_for_advertising_use_cases.md#conversion-lift-measurement)) when it would be useful to display content from different partitions in the same page. This can only be allowed if the documents that contain data from different partitions are isolated from each other such that they're visually composed on the page, but unable to communicate with each other. Iframes do not suit this purpose since they have several communication channels with their embedding frame (e.g., postMessage, URLs, size attribute, name attribute, etc.). We propose fenced frames, a new element to embed documents on a page, that explicitly prevents communication between the embedder and the frame.
34 |
35 | ## Goals
36 |
37 | The fenced frame enforces a boundary between the embedding page and the cross-site embedded document such that user data visible to the two sites is not able to be joined together. This can be helpful in preventing user tracking or other privacy threats.
38 |
39 | **Caveat:** It could still be possible for documents colluding via covert channels to be able to communicate information (See [Ongoing technical constraints](#ongoing-technical-constraints) for more details).
40 |
41 | The privacy threat addressed is:
42 |
43 | **The ability to correlate the user’s identity/information on the embedding site with that on the embedded site.**
44 |
45 | The different use cases and their privacy models are discussed [here](https://github.com/WICG/fenced-frame/blob/master/explainer/use_cases.md).
46 |
47 | ## Design
48 |
49 | Fenced frames are embedded contexts that have the following characteristics to prevent embedder identifiers from being joined with identifiers from the embedded site:
50 |
51 |
52 |
53 | * They’re not allowed to communicate with the embedder and vice-versa, except for certain information such as limited size information.
54 | * They access storage and network via unique partitions so no other frame outside a given fenced frame document can share information via these channels. This is described [here](https://github.com/WICG/fenced-frame/blob/master/explainer/storage_cookies_network_state.md).
55 | * They may have access to browser-managed, limited unpartitioned user data, for example, turtledove interest group.
56 |
57 | The idea is that the fenced frame should not have access to both of the following pieces of information and be able to exfiltrate a join on those:
58 |
59 |
60 |
61 | * User information on the embedding site
62 | * Accessible via communication channels
63 | * Information from other top-site partitions
64 | * Accessible via an API (e.g., Turtledove) or via access to unpartitioned storage
65 |
66 |
67 | A primary use case for fenced frames is to load content that depends on values in another partition’s storage. For example, in Turtledove, we pick an ad based on the user's interest groups (which are joined while browsing other sites) and load it in a fenced frame. The URL of the ad reflects the user's interest group memberships, which is a form of cross-site data, therefore we store the URL for the ad creative _opaquely_ in a fenced frame config (details [here](https://github.com/WICG/fenced-frame/blob/master/explainer/fenced_frame_config.md)). The embedder can use this object to load the ad resulting from the Turtledove auction, but can't inspect it to determine _which_ ad won.
68 |
69 | We expect some leakage of information to be possible via network timing attacks. The side channel and some mitigations are described [here](https://github.com/WICG/fenced-frame/blob/master/explainer/network_side_channel.md).
70 |
71 | ### Fenced frame API
72 |
73 | The proposed fenced frame API is to have a new element type and treat it as a [top-level browsing context](https://html.spec.whatwg.org/#top-level-browsing-context). This section details this approach and later we also describe the alternative API approaches that were considered.
74 |
75 |
76 | #### New element type - a top-level browsing context
77 |
78 | In this approach, a fenced frame behaves as a top-level browsing context that is embedded in another page. This is aligned with the model that a fenced frame is similar to a “tab” since it has minimal communication with the embedding context and is the root of its frame tree and all the frames within the tree can communicate normally with each other.
79 | Since fenced frames are embedded frames, they also behave like iframes in many ways. For example:
80 | * Browser extensions will access a fenced frame as an iframe, e.g., for ad blocking.
81 | * Browser features like accessibility, developer tools etc. will access a fenced frame like an iframe.
82 |
83 |
84 | ##### Example usage
85 |
86 |
87 | ```
88 | const fencedframe = document.createElement('fencedframe');
89 | fencedframe.config = new FencedFrameConfig('demo_fenced_frame.html');
90 | ```
91 |
92 | * Browser lets the server know via a new [`sec-fetch-dest`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Sec-Fetch-Dest) header value `fencedframe` to let it know that a request is from a fenced frame tree.
93 | * The server needs to opt-in to be loaded in a fenced frame or in an iframe embedded in a fenced frame tree. Without an opt-in, the document cannot be loaded. For opt-in, we use the [supports-loading-mode](https://github.com/jeremyroman/alternate-loading-modes/blob/main/opt-in.md#declaration) header with a new value of `fenced-frame`.
94 |
95 | ##### Benefits over nested browsing context
96 |
97 |
98 |
99 | * Simpler to spec since the fenced frame aligns well with a top-level browsing context.
100 | * Simpler to achieve the communications restrictions with the embedding context, being a top-level browsing context.
101 |
102 |
103 | ##### Downsides of a new element
104 |
105 |
106 |
107 | * Existing sites need to change to embed a new element. This is not really a downside though, since even if it was an enhancement to the iframe element, existing sites would have needed to make changes either in the attributes or in the headers to differentiate it from a regular iframe.
108 |
109 |
110 | ### Fenced frame tree
111 |
112 | A fenced frame is the root of the fenced frame tree. The root fenced frame and any child iframes in this tree are not allowed to use communication channels to talk to frames outside the tree or vice-versa. The frames within the tree can communicate with each other normally.
113 |
114 | ### Information channel between fenced frame and other frames
115 |
116 | There are many channels between the fenced frame tree and the other frames that will need to be restricted and a few of them are listed below:
117 |
118 |
119 |
120 | * PostMessage
121 | * Name, allow, cspee and other attributes
122 | * Resize
123 | * Access to window.parent/ window.top etc.
124 | * Events fired simultaneously in the embedding context and the fenced frame such as page lifecycle events like onload
125 | * …
126 |
127 | This discussion assumes that third-party cookies, like all other third party storage, are also [disallowed](https://blog.chromium.org/2020/01/building-more-private-web-path-towards.html) or else those would be a communication channel between the fenced frame and the embedding site.
128 |
129 | ### HTMLFencedFrameElement class
130 |
131 | All fenced frame related functions will live in its own class, in the same way that iframe-related funcionality lives in HTMLIFrameElement.
132 |
133 | #### Can Load API
134 |
135 | There are various reasons a fenced frame config with an opaque url could refuse to load in a page. For example, if the page is not in a secure context, or if CSPEE is specified in the embedding frame, the fenced frame config will refuse to load. This is a lot for a developer to keep track of.
136 |
137 | If the process of getting an ad in the page is complex or expensive, there needs to be a way to ensure that the resulting ad will actually end up in the page before the expensive process begins.
138 |
139 | A static API method will be introduced to the HTMLFencedFrameElement class to check this. No fenced frame will be created when calling this API, and it can be invoked before actually attempting to load a fenced frame config. The API will return a boolean, true if a config with an opaque mapped url would be able to load in the caller's context, false if not.
140 |
141 | ##### Example usage
142 |
143 | ```
144 | HTMLFencedFrameElement.canLoadOpaqueURL();
145 | ```
146 | ```
147 | > true
148 | ```
149 |
150 | This is called synchronously, and will look at the execution context of the frame invoking the API.
151 |
152 | ## Security considerations
153 |
154 | Even though a fenced frame is isolated from its embedding context, it cannot be used as a workaround to the security restrictions that the top-level site wants to enforce on the embedding frames, without the knowledge of the top-level site. The design decisions of fenced frames related to security mechanisms like sandbox, csp, permission policy etc. are based on the following principles:
155 | * Attributes like cspee, sandbox etc. and headers like frame-ancestors etc. cannot be used as a communication channel with the embedding context.
156 | * Fenced frame should not be able to escalate privileges without the knowledge of the top-level site. A feature disallowed by a top-level site for a given origin cannot be allowed in a fenced frame of that same origin, since that becomes a security risk. Since this requires the fenced frame learning about what features are allowed for its origin, and since revealing to the fenced frame what features are enabled for what origins are a fingerprinting vector, all permission policy delegation based features are therefore disallowed in fenced frames that don't allow data infiltration. Fenced frames that do allow for data infiltration (i.e. ones loaded with transparent URLs allowing for arbitrary data to be added) can use and inherit permissions policy delegation for features. See: [Fenced frames: permissions and document policies](https://github.com/WICG/fenced-frame/blob/master/explainer/permission_document_policies.md).
157 | * There are headers from the fenced frame site that are not honored as they would in an iframe, e.g. frame-ancestors, due to being a privacy leak. This is the reason fenced frames need to be opted in by the site using the opt-in response header.
158 |
159 | More about security mechanisms are detailed in:
160 | * [Fenced frames and CSP](https://github.com/WICG/fenced-frame/blob/master/explainer/interaction_with_content_security_policy.md)
161 | * [Fenced frames and policies](https://github.com/WICG/fenced-frame/blob/master/explainer/permission_document_policies.md)
162 | * [Fenced frames and sandbox](https://docs.google.com/document/d/1RO4NkQk_XaEE7vuysM9LJilZYsoOhydfh93sOvrPQxU/edit?usp=sharing)
163 |
164 | **Secure contexts:** Fenced Frames are only allowed if all ancestor frames are [secure contexts](https://w3c.github.io/webappsec-secure-contexts/), the fenced frame's document is from a [potentially trustworthy URL](https://w3c.github.io/webappsec-secure-contexts/#potentially-trustworthy-url) and all subresources inside the FF will follow [mixed mode restrictions](https://web.dev/fixing-mixed-content/).
165 |
166 | **Inheritance for local resources:** Documents hosting [local](https://fetch.spec.whatwg.org/#is-local) resources inherit their [policy containers](https://html.spec.whatwg.org/multipage/origin.html#policy-container) from their initiator or parent document, however for fenced frames, no such inheritance will take place. Fenced frames hosting local Documents will have a fresh policy container as they were created with no initiator document, just like the first Document in a top-level browsing context created with no initiator document.
167 |
168 | **xsleaks:** In terms of cross site leak attacks, fenced frames is at least as secure as iframes are and better in some cases by default e.g. always having noopener, no joint history etc. For more details, the fenced frames xsleaks audit can be found [here](https://docs.google.com/spreadsheets/d/1YkQxcQlOd24XmSUQ8RpQU0zINYSTTih8drNibV0LIXE/edit?usp=sharing).
169 |
170 | **Process isolation:** Process isolation for fenced frames is detailed [here](https://github.com/WICG/fenced-frame/blob/master/explainer/process_isolation.md).
171 |
172 |
173 | ## Privacy considerations
174 |
175 | The fenced frame’s main goal is to improve privacy by disallowing communication with the embedder. There are however some attributes that might need to be shared between the two and their privacy impact needs to be carefully considered and mitigated, if possible. Some of these attributes are:
176 |
177 |
178 | * **Initial size and resize:** The API that generates a fenced frame config can pick the initial size that the fenced frame document sees, subject to whatever restrictions it deems necessary for its privacy model. If the initial size is fixed, then any changes the embedder attempts to make to the fenced frame's size will not be reflected inside of it.
179 | * **Intersection Observer:** See [Integration with web platform > Viewability](https://github.com/WICG/fenced-frame/blob/master/explainer/integration_with_web_platform.md#Viewability) for discussion of the privacy considerations for the Intersection Observer API.
180 | * **Delegated permissions:** [Permission delegation](https://www.chromestatus.com/feature/5670617353289728) restricts permission requests to the top-level frame. Since fenced frames are embedded contexts, they should not have access to permissions, even if they are treated as top-level browsing contexts. Delegation of permissions from the embedding context to the fenced frame is a data infiltration channel, and should be disallowed when the privacy story disallows data inflow. This is detailed further [here](https://github.com/WICG/fenced-frame/blob/master/explainer/permission_document_policies.md).
181 | * **Network side channel:** This is detailed more here: [network side channel](https://github.com/WICG/fenced-frame/blob/master/explainer/network_side_channel.md)
182 | * **Navigation url:** Since fenced frames are allowed to open popups or navigate the top-level page in some use cases, gated on user activation, the navigation url can carry bits of information out of the fenced frame tree. If the embedder and the destination are same-origin, the information in the url and embedder's info can be joined locally on navigation. This might need mitigations going forward (currently being brainstormed); we plan to add metrics to understand how often this happens in practice. Additionally, this is vulnerable to the network side channel as mentioned above if the embedding site and destination site are colluding (even when not same-origin)---though this is less concerning than for non-navigation network requests, since navigation is only allowed upon user interactions like clicks.
183 |
184 | More of these channels exist and the [integration with web platform](https://github.com/WICG/fenced-frame/blob/master/explainer/integration_with_web_platform.md) details them further.
185 |
186 | ### Ongoing technical constraints
187 | Fenced frames disable explicit communication channels, but it is still possible to use covert channels to share data between the embedder and embeddee, e.g. global socket pool limit (as mentioned in the [xsleaks audit](https://docs.google.com/spreadsheets/d/1YkQxcQlOd24XmSUQ8RpQU0zINYSTTih8drNibV0LIXE/edit?usp=sharing)), network side channel and intersection observer as described above, etc. Mitigations to some of these are being brainstormed. We also believe that any use of these known covert channels is clearly hostile to users and undermines web platform intent to the point that it will be realistic for browsers to take action against sites that abuse them.
188 |
189 | ## Parallels with Cross-site portals
190 |
191 | [Portals](https://wicg.github.io/portals/) allow for rendering of, and seamless navigation to, embedded content.
192 |
193 | If the embedded content is cross-site, the privacy threat of joining user identities on the two sites exists before the user ever engages with the portal. The privacy threat for portals is further detailed [here](https://github.com/WICG/portals#privacy-threat-model-and-restrictions).
194 |
195 | Portal is a separate element type than a fenced frame, but requires very similar restrictions in its communication with the embedding context as a fenced frame. It is thus likely that portals and fenced frames will converge on their cross-site tracking mitigations to a large extent.
196 |
197 | ## API alternatives considered
198 |
199 | Both of the alternatives given in this section are applicable only if fenced frames were a type of iframe. As already described in the document above, they have the downside of spec and browser implementation complexity as many iframe capabilities will need to be special-cased for fenced frames.
200 |
201 | #### Using iframe with document policy
202 |
203 | In this alternative approach the fenced frame is a nested browsing context with the communications restrictions placed on an iframe via a [document policy](https://github.com/w3c/webappsec-feature-policy/blob/master/document-policy-explainer.md).
204 |
205 |
206 | Example usage
207 |
208 | ```
209 |
211 | ```
212 |
213 |
214 | The embedding site sends the following header in the request when creating the fenced frame root frame:
215 |
216 |
217 | ```
218 | Sec-Required-Document-Policy: fenced-frame-tree;root=true
219 | ```
220 |
221 |
222 | The server then responds back using the following header if it complies with the restrictions (otherwise the document will fail to load with an error).
223 |
224 |
225 | ```
226 | Document-Policy: fenced-frame-tree;root=true
227 | ```
228 |
229 | The fenced frame tree’s root frame is created using the root parameter’s value as true while any frames nested within it is set by the browser as having the root param set to false, unless a nested fenced frame tree is created where the root parameter is again set to true. Both true and false values of the root parameter are considered of equal strictness and thus it is possible to continue a fenced frame tree or start a new one.
230 |
231 |
232 | Benefits
233 |
234 |
235 |
236 | * Able to work with the existing “iframe” element
237 |
238 |
239 | Downsides
240 |
241 |
242 |
243 | * Much more complex spec and browser implementation since many iframe features will need to be special-cased for fenced frames.
244 |
245 | There were other alternatives considered for the iframe approach like using feature policy or a new attribute, detailed later in this document.
246 |
247 |
248 | #### Using a new iframe attribute
249 |
250 | Another way that was considered for this primitive was to have a new iframe attribute, say “fenced frame”.
251 |
252 |
253 | ```
254 |
255 | ```
256 |
257 |
258 | Benefits
259 |
260 |
261 |
262 | * A single attribute defines the fenced frame so it is simpler to use by developers
263 |
264 | Downsides
265 |
266 |
267 |
268 | * Iframe already has existing configuration attributes like sandbox, allow and policy and adding a fourth would lead to complexity in terms of how they all interact among each other.
269 |
270 | #### Using Feature policy/Permission policy
271 |
272 | We considered using [feature policy](https://developers.google.com/web/updates/2018/06/feature-policy) attributes for network, storage and communication channels using the ‘allow’ keyword, instead of document policy.
273 |
274 | Benefits over using document policy
275 |
276 |
277 |
278 | * Does not need HTTP header exchange as in document policy
279 |
280 | Downsides
281 |
282 |
283 |
284 | * Feature policy’s objective is the delegation of powerful feature permissions to trusted origins while a fenced frame requires general features like inter-frame communication to be restricted on documents.
285 | * Since there is no HTTP header exchange there are more chances of site breakage due to restricting common features like inter-frame communications.
286 |
287 |
288 |
289 |
290 |
291 |
292 |
293 |
294 |
--------------------------------------------------------------------------------
/explainer/TAG_Security_Privacy_Questionnaire.md:
--------------------------------------------------------------------------------
1 | ### **TAG Security/Privacy Questionnaire**
2 |
3 | This section contains answers to the [W3C TAG Security and Privacy](https://w3ctag.github.io/security-questionnaire/) [Questionnaire](https://w3ctag.github.io/security-questionnaire/).
4 |
5 |
6 |
7 | 1. What information might this feature expose to Web sites or other parties, and for what purposes is that exposure necessary?
8 |
9 | Fenced frames can be viewed as a more private and restricted iframe. As such, the element does not inherently expose any new information to web sites or third parties. However, Fenced Frames are intended to contain data that belongs to a partition other than the top-frame’s storage partition e.g. the rendering url in [TURTLEDOVE](https://github.com/w3ctag/design-reviews/issues/723) reflects the interest group of the user which may be derived from user activity in another partition. Therefore it is necessary for the Fenced Frame to attempt to block communications between the fenced frame and its embedder. Fenced frames remove explicit web-platform communication channels between the two (such as postMessage) and many side channels e.g. size, programmatic focus, policy delegation etc. but some side channels still exist and we will continue to work to mitigate them.
10 |
11 | 2. Do features in your specification expose the minimum amount of information necessary to enable their intended uses?
12 |
13 | Yes, see above answer for ways information exposure is minimized.
14 |
15 | 3. How do the features in your specification deal with personal information, personally-identifiable information (PII), or information derived from them?
16 |
17 | Fenced frames do not inherently provide personal information, PII or information derived from them.
18 |
19 | 4. How do the features in your specification deal with sensitive information?
20 |
21 | Same answer as # 3.
22 |
23 | 5. Do the features in your specification introduce a new state for an origin that persists across browsing sessions?
24 |
25 | No.
26 |
27 | 6. Do the features in your specification expose information about the underlying platform to origins?
28 |
29 | No
30 |
31 | 7. Does this specification allow an origin to send data to the underlying platform?
32 |
33 | No
34 |
35 | 8. Do features in this specification allow an origin access to sensors on a user’s device
36 |
37 | No
38 |
39 | 9. What data do the features in this specification expose to an origin? Please also document what data is identical to data exposed by other features, in the same or different contexts.
40 |
41 | Same answer as #1.
42 |
43 | 10. Do features in this specification enable new script execution/loading mechanisms?
44 |
45 | No
46 |
47 | 11. Do features in this specification allow an origin to access other devices?
48 |
49 | No
50 |
51 | 12. Do features in this specification allow an origin some measure of control over a user agent’s native UI?
52 |
53 | No
54 |
55 | 13. What temporary identifiers do the features in this specification create or expose to the web?
56 |
57 | None.
58 |
59 | 14. How does this specification distinguish between behavior in first-party and third-party contexts?
60 |
61 | Fenced frames are always present as embedded frames. In terms of communication restrictions from the embedding page, they behave almost like a separate tab but fenced frames do not get access to first-party storage/cookies. A fenced frame document gets access to a nonce-based ephemeral cookie and storage partition.
62 |
63 | 15. How do the features in this specification work in the context of a browser’s Private Browsing or Incognito mode?
64 |
65 | No difference with a regular mode browser
66 |
67 | 16. Does this specification have both "Security Considerations" and "Privacy Considerations" sections?
68 |
69 | Yes.
70 |
71 | 17. Do features in your specification enable origins to downgrade default security protections?
72 |
73 | No
74 |
75 | 18. What should this questionnaire have asked?
76 |
77 | N/A
78 |
--------------------------------------------------------------------------------
/explainer/fenced_frame_config.md:
--------------------------------------------------------------------------------
1 | # Fenced frame configs
2 |
3 |
4 | ## Introduction
5 |
6 | For use cases involving APIs that access cross-site data, we need to be able to load a fenced frame with content determined by the API without revealing information about the content to the embedding context. For example, with interest-based ads in [Protected Audience](https://github.com/WICG/turtledove), the winning ad that's returned from the auction depends on the user's cross-site interest group data, which we don't want to expose to the site that calls the auction. This document proposes a web-platform way of loading content into a fenced frame using an opaque object.
7 |
8 |
9 | ## Proposed solution
10 |
11 | `` has an attribute `config`, rather than `src`. APIs like FLEDGE return a `FencedFrameConfig` object defined by our [WebIDL](https://wicg.github.io/fenced-frame/#fenced-frame-config-interface). This object has a series of fields that specify the behavior desired by the API (e.g. the ad url, width and height as seen from within the fenced frame, etc.). When the embedder stores this object into the `config` attribute, the fenced frame loads the context accordingly.
12 |
13 | In order to hide information as described above, the browser _redacts_ `FencedFrameConfig` before sending it to the embedder. This means that certain fields which are sensitive, like the ad url, are replaced with a string `opaque`. The embedder may see whether there is a value defined for that field, but not what the value is. Likewise, when the embedder requests that a config be loaded into the fenced frame, the browser is responsible for looking up the config in a data structure in order to access the unredacted information.
14 |
15 |
16 | ### Protected Audience Example
17 |
18 | When the SSP JS invokes the Protected Audience API to run the ad auction, it gets back the `FencedFrameConfig` as the result, which is then used for rendering the fenced frame. This `FencedFrameConfig` has an opaque `src`, which maps to an actual ad url which is part of the interest group.
19 |
20 |
21 | ```
22 | navigator.runAdAuction(myAuctionConfig).then((auctionWinnerConfig) => {
23 | // auctionWinnerConfig value e.g.
24 | // FencedFrameConfig {
25 | // 'src': 'opaque' ('ad.com/foo' internally)
26 | // ...
27 | // }
28 | const adFrame = document.createElement('fencedframe');
29 | adFrame.config = auctionWinnerConfig;
30 | });
31 | ```
32 |
33 | ## Backwards compatibility
34 |
35 | Previously we used a [`urn:uuid`](https://tools.ietf.org/html/rfc4122) and the `src` attribute to accomplish this same behavior. We will continue to support `urn:uuid` and `src` for a transition period.
36 | Update: The `src` attribute is no longer supported on fenced frames.
37 |
38 | ## Embedder context
39 |
40 | After creating the config and prior to loading the config into the fenced frame, an embedder can communicate a string of contextual information to one or more worklets for the [Shared Storage API](https://github.com/WICG/shared-storage) that are spun up inside the fenced frame (details [here](https://github.com/WICG/fenced-frame/blob/master/explainer/fenced_frame_config_context.md)). This can be useful for reporting purposes.
41 |
--------------------------------------------------------------------------------
/explainer/fenced_frame_config_context.md:
--------------------------------------------------------------------------------
1 | # Embedder context in fenced frame configs
2 |
3 | ## Introduction
4 |
5 | For use cases that leverage the [Private Aggregation API](https://github.com/patcg-individual-drafts/private-aggregation-api) to report on advertisements within fenced frames, it might be important to tie certain information available inside the fenced frame, e.g. viewability or performance, to some contextual information from the embedding publisher page, such as an event-level ID.
6 |
7 | In a scenario where the input URLs for the fenced frame are required to be k-anonymous, however, such as when creating a [FencedFrameConfig](https://github.com/WICG/fenced-frame/blob/master/explainer/fenced_frame_config.md) from running a [FLEDGE auction](https://github.com/WICG/turtledove/blob/main/FLEDGE.md#2-sellers-run-on-device-auctions), trying to communicate the event-level ID to the fenced frame by attaching an identifier to any of the input URLs would make it difficult for any input URL(s) with the attached identifier to reach the k-anonymity threshold.
8 |
9 | ## Proposed solution
10 |
11 | Instead, before navigating the fenced frame to the auction's winning [FencedFrameConfig](https://github.com/WICG/fenced-frame/blob/master/explainer/fenced_frame_config.md), the embedder could write the event-level ID, or other contextual information, as a string, by using the [FencedFrameConfig](https://github.com/WICG/fenced-frame/blob/master/explainer/fenced_frame_config.md)'s `setSharedStorageContext()` method, as in the example below.
12 |
13 | Subsequently, anything written through the [FencedFrameConfig](https://github.com/WICG/fenced-frame/blob/master/explainer/fenced_frame_config.md)'s `setSharedStorageContext()` method prior to a fenced frame's navigation to that config, can be read via `sharedStorage.context` from inside a worklet for the [Shared Storage API](https://github.com/WICG/shared-storage) created by the fenced frame or by any of its same-origin descendants. That is, `sharedStorage.context` can be invoked via the script for a operation previously registered through [`sharedStorage.worklet.addModule()`](https://github.com/WICG/shared-storage/tree/main#outside-the-worklet) and then called by [`sharedStorage.run()`](https://github.com/WICG/shared-storage/tree/main#outside-the-worklet).
14 |
15 | ## Example
16 |
17 | After the SSP's JavaScript invokes the [Turtledove/FLEDGE API](https://github.com/WICG/turtledove/blob/main/FLEDGE.md) and receives the resulting [FencedFrameConfig](https://github.com/WICG/fenced-frame/blob/master/explainer/fenced_frame_config.md) `fencedFrameConfig`, the SSP JS can write an event ID string via `fencedFrameConfig.setSharedStorageContext()`.
18 |
19 | In the embedder page:
20 |
21 | ```js
22 | // See https://github.com/WICG/turtledove/blob/main/FLEDGE.md for how to write an auction config.
23 | const auctionConfig = { ... };
24 |
25 | // Run a FLEDGE auction, setting the option to "resolveToConfig" to true.
26 | auctionConfig.resolveToConfig = true;
27 | const fencedFrameConfig = await navigator.runAdAuction(auctionConfig);
28 |
29 | // Write to the config any desired embedder contextual information as a string.
30 | fencedFrameConfig.setSharedStorageContext("My Event ID 123");
31 |
32 | // Navigate the fenced frame to the config.
33 | document.getElementById('my-fenced-frame').config = fencedFrameConfig;
34 | ```
35 |
36 | The reporting JavaScript in the fenced frame would spin up a [shared storage worklet](https://github.com/WICG/shared-storage#in-the-worklet-during-an-operation), in order to make a report about some information `frameInfo` that is only available within the fenced frame, and that needs to be tied back to the event ID string for reporting purposes.
37 |
38 | In the fenced frame (`my-fenced-frame`):
39 |
40 | ```js
41 | // Save some information we want to report that's only available inside the fenced frame.
42 | const frameInfo = { ... };
43 |
44 | // Create a shared storage worklet.
45 | // See https://github.com/WICG/shared-storage#proposed-api-surface for more details
46 | // about creating and running shared storage worklets.
47 | await window.sharedStorage.worklet.addModule('report.js');
48 |
49 | // Send a report using shared storage and private aggregation.
50 | await window.sharedStorage.run('send-report', {
51 | data: { info: frameInfo },
52 | });
53 | ```
54 |
55 | Inside the [shared storage worklet](https://github.com/WICG/shared-storage#in-the-worklet-during-an-operation) itself, the event ID can be retrieved and sent in a aggregatable report via the [Private Aggregation API](https://github.com/patcg-individual-drafts/private-aggregation-api).
56 |
57 | In the worklet script (`report.js`):
58 |
59 | ```js
60 | // See https://github.com/WICG/shared-storage#in-the-worklet-during-addmodule for more
61 | // details about registering shared storage operations.
62 | class ReportingOperation {
63 | async run(data) {
64 | // Helper functions that map the event ID to a predetermined bucket and the frame info
65 | // to an appropriately-scaled value.
66 | // See also https://github.com/patcg-individual-drafts/private-aggregation-api#examples
67 | function convertEventIdToBucketId(eventId) { ... }
68 | function convertFrameInfoToValue(info) { ... }
69 |
70 | // The user agent sends the report to the reporting endpoint of the script's
71 | // origin (that is, the caller of `sharedStorage.run()`) after a delay.
72 | privateAggregation.sendHistogramReport({
73 | bucket: convertEventIdToBucketId(sharedStorage.context) ,
74 | value: convertFrameInfoToValue(data.info)
75 | });
76 | }
77 | }
78 | register('send-report', ReportingOperation);
79 | ```
80 |
--------------------------------------------------------------------------------
/explainer/fenced_frames_with_local_unpartitioned_data_access.md:
--------------------------------------------------------------------------------
1 | # **Fenced Storage Read API**
2 | **(earlier referred to as Fenced Frames with local unpartitioned data access)**
3 |
4 |
5 | ## Introduction and Goals
6 |
7 | There are situations in which it is helpful to decorate a third-party widget with cross-site information about the user, such as a personalized payment button that displays credit card information to give the user confidence that the payment flow will be smooth, or a personalized sign-in button. Showing a personalized button gives the user more context about what they can expect will happen when they click on the button, allowing them to make a more informed choice, closer to their preferences. These sorts of use cases will be broken in scenarios when [third-party cookies are not available](https://privacysandbox.com/news/privacy-sandbox-update/).
8 |
9 | Fenced frames are a natural fit for such use cases, as they allow for frames with cross-site data to be composed within a page of another partition. The idea proposed here is to allow fenced frames to have access to the cross-site data stored for the given origin within [shared storage](https://github.com/WICG/shared-storage). In other words, the payment site would add the user’s payment data to shared storage when the user visits the payment site, and then read it in third-party fenced frames to decorate their payment button.
10 |
11 | To prevent the fenced frame from leaking the user’s shared storage data out (to the embedder or to servers via network) we’re requiring that fenced frames first disable all untrusted network[1] communications before accessing shared storage. Note that the eventual intent is that all fenced frames would disable untrusted network communication anyway, so this isn’t exactly new, but it’s particularly vital that we do so when allowing access to shared storage since it has unbounded cross-site information available to it, compared to e.g. k-anonymous cross-site information available in Protected Audience ads.
12 |
13 | The motivation for this variant of fenced frames are customized payment buttons for third-party payment service providers (as discussed in this [issue](https://github.com/WICG/fenced-frame/issues/15)) but this proposal is not intended to be restricted to payments. Any form of third-party UX that wishes to show personalized information to a user, without leaking that information to the embedder, could use it.
14 |
15 | [1] More details [below](#why-untrusted-network) on untrusted vs trusted network
16 |
17 |
18 | ## User experience
19 |
20 | It’s been [noted](https://developers.googleblog.com/2021/05/updated-google-pay-button-increases-click-through-rates.html) that personalized payment buttons (buttons that display saved payment methods) can significantly increase click-through rates as it provides increased confidence that the checkout flow will be smooth. With this proposal, users will continue to see the personalized button.
21 |
22 | ## Information flow and design
23 |
24 | The sequence of steps to achieve the user experience as mentioned above are given below:
25 |
26 |
27 |
28 | 1. **Same as today:** A user visits the payment provider’s site as a first party and enters their payment method details, for later use on merchant sites.
29 | 2. **New:** After step 1, the payment provider decides what information is needed from the user's payment method details to render in a subsequent personalized button and writes it to shared storage (reasons for selecting shared storage discussed [here](#shared-storage-access)). For example, if the user details needed in the personalized payment button are the last 4 digits of the user’s credit card, the JS on the payment provider’s page will invoke the following:
30 |
31 | ```
32 | window.sharedStorage.set("last-4-digits", value)
33 | ```
34 |
35 |
36 | 3. **Same as today:** The user, at a later time, visits the merchant’s site and the payment provider’s script, say, payment.js, on the merchant site decides to create a personalized button. Note that payment.js runs on the merchant’s page, either directly on the 1p page or in an iframe belonging to the payment provider.
37 | 4. **New:** For creating the personalized button, payment.js creates a fenced frame instead of an iframe as it does today, using the following FencedFrameConfig constructor:
38 |
39 | ```
40 | fenced_frame = new FencedFrameConfig('https://examplepay.com/button.html?');
41 | ```
42 |
43 |
44 |
45 | **Privacy impact:** Note that it is ok for the source of the fenced frame to carry arbitrary data from the creator context because that data cannot be joined with cross-site data until after the network is restricted.
46 |
47 | 5. **New: State 1 of the fenced frame (full network):** In this state, when the fenced frame is created, it doesn’t have any unpartitioned data access. The fenced frame in this state has unrestricted network access which can be utilized to load the HTML document and subresources in the fenced frame (such as the button image).
48 |
49 | Note that in this state the content in the fenced frame is not personalized, since it hasn’t accessed any unpartitioned data.
50 |
51 | 6. **New: State 2 of the fenced frame (disable untrusted network):** In order to access unpartitioned data to personalize the contents of the fenced frame, the fenced frame needs to block off network access. As mentioned above in the privacy impact section this will prevent any data joined across the embedding site and the fenced frame site from exfiltrating. There will be a new API introduced to do that: `window.fence.disableUntrustedNetwork();`
52 | 7. **New: State 2 of the fenced frame (access unpartitioned data):** Finally after exfiltration channels have been blocked off via window.fence.disableUntrustedNetwork(), the FF content can access unpartitioned data via shared storage. The data that was written to shared storage in step 2, can now be read and rendered to personalize the content via the `sharedStorage.get()` method.
53 |
54 | There we go: the user now sees the personalized button!!
55 |
56 |
57 |
58 | 8. **Same as today:** If the user wants to proceed with the payment transaction, they can click on the button.
59 | 9. **New:** To proceed with the payment transaction, payment.js needs to be able to listen to the click that happened on the button via the new click API surface described [here](#click-listener-api).
60 | 10. **Same as today:** Once the information that a click happened inside the FF is available in the embedding context, the payment.js opens a new top-level context as a new tab or invokes the [Payment Handler API](https://developer.mozilla.org/en-US/docs/Web/API/Payment_Handler_API) and the transaction proceeds as it does today.
61 |
62 |
63 | ## Shared Storage Access
64 |
65 | This section goes into why we chose shared storage for this solution. We considered alternatives like Cookies and Local Storage as described in the “Alternatives considered” section.
66 |
67 |
68 | ### **Data Requirements**
69 |
70 | Let’s first go through the requirements on the data, taking the example of examplepay.com as the payment provider:
71 |
72 |
73 |
74 | * **Readability**: The data needs to be readable in an examplepay.com fenced frame (after untrusted network has been revoked) and not in an examplepay.com top-level frame or iframe.
75 | * The data only needs to be read inside the FF from JS and not via network headers.
76 | * **Writability**: The data only needs to be writable in a 1p context from examplepay.com.
77 | * **Scope:** The data should be scoped to the origin examplepay.com.
78 | * **Size:** As per the current use case feedback, the data itself is not very large, is text-only and can fit in a normal cookie size. This is subject to change though, e.g. if a payment provider wants to also show the user's profile picture in the personalized button.
79 | * **Unpartitioned:** The data only needs to be accessed in the unpartitioned state and not in a partitioned (by top-level site i.e. the merchant site for payment providers) state.
80 |
81 | Shared Storage is a Privacy Sandbox API that allows unpartitioned storage access with restricted output gates as described [here](https://github.com/WICG/shared-storage/blob/main/README.md). The existing output gates for shared storage are private aggregation report and opaque URL selection. The proposal here is to introduce a new output gate: **read from a fenced frame after network revocation**.
82 |
83 | Some of the enhancements needed for shared storage to be used for this proposal are:
84 |
85 |
86 |
87 | * Create a new shared storage output gate: a fenced frame with no network. Privacy-wise this is safe, since the unpartitioned data being read is not exfiltrated either to the embedding context (because of the nature of fenced frames) or via writing to any unpartitioned storage (like local storage, since FFs have a unique, ephemeral storage partition) or via network.
88 | * The API `sharedStorage.get('')` will need to be enabled inside the fenced frame and can only resolve successfully once the network is cut-off.
89 |
90 | Note that there is no change needed to Shared Storage write mechanisms, since it is already writable from both 1p and 3p contexts and this use case only needs 1p write access.
91 |
92 | **Benefits**
93 |
94 | The benefits of using shared storage for this solution are primarily how it aligns with all of the requirements mentioned above.
95 |
96 |
97 |
98 | * The incremental enhancements to shared storage for this use case aren’t complicated/hard to integrate in the existing shared storage design/implementation.
99 | * Shared storage, is by definition, unpartitioned data. There is no notion of partitioned data for this use case as mentioned in the Requirements section above, which makes it more aligned with using shared storage.
100 | * Shared storage is by JS-readable only and thus aligns with the requirement.
101 | * Additionally, the invocation of Shared Storage get() API for this feature is gated behind [enrollment and attestation](https://developers.google.com/privacy-sandbox/private-advertising/enrollment) which offers additional, policy-based, privacy protection. Note a new enrollment and attestation category will be added specifically for accessing unpartitioned data within the fenced frame tree named “Fenced Storage Read API”.
102 |
103 | **Downsides**
104 |
105 |
106 |
107 | * Browser compatibility: Even though other browsers should not see concerns with the privacy aspect of this output gate of shared storage, it’s more work to implement for other browsers than exposing cookies or localStorage to a fenced frame since they haven’t implemented shared storage yet. See the LocalStorage section to see mitigations for this.
108 |
109 |
110 | ## Revoking Network Access
111 |
112 | At a high level, the new API window.fence.disableUntrustedNetwork() allows a fenced frame to disable exfiltrating information (primarily via network requests) in exchange for gaining access to unpartitioned data via sharedStorage reads.
113 |
114 | Some more details:
115 |
116 |
117 |
118 | * Even though this information flow only relies on fenced frames created using the url constructor, we would not be disallowing the usage of window.fence.disableUntrustedNetwork() in FFs created using the other APIs as the eventual intent is for all fenced frames to have restricted network access. It will be available from fenced frames whose configs are generated by all current methods:
119 | * navigator.runAdAuction(...)
120 | * sharedStorage.selectURL(...)
121 | * FencedFrameConfig(url)
122 | * As a result of invoking this API, network is disabled in the complete fenced frame tree, i.e. in the root fenced frame and any of its embedded iframes.
123 | * We are still thinking about how disableUntrustedNetwork() should interact with nested fenced frames. The alternatives are:
124 | * Any embedded fenced frames within the parent FF should also have invoked disableUntrustedNetwork() before the parent FF can access shared storage.
125 | * Calling disableUntrustedNetwork() disables network for all child fenced frames.
126 | * Disabling network disables the following:
127 | * **Subresources requests**: This includes resource requests like for scripts, images etc. or APIs like sendBeacon, etc.
128 | * **Navigation requests:** This implies that there cannot be a navigation initiated either for loading a document inside the FF tree or for navigating the top-level page or opening a new tab/window.
129 | * **Event level reporting:** Any event level reporting mechanisms supported in fenced frames, i.e., [fenced frames ads reporting](https://github.com/WICG/turtledove/blob/main/Fenced_Frames_Ads_Reporting.md).
130 | * **Any other network channels:** This includes any channel not covered in the above categories like WebSocket, web workers, etc.
131 | * **In-progress network requests:** Allowing in-progress network requests to continue could lead to inadvertent privacy leaks. For example, a possible attack could arise where multiple requests are initiated and, based on unpartitioned data, some of them are canceled via the [abort API](https://developer.mozilla.org/en-US/docs/Web/API/AbortController). Therefore the proposal is to cancel any ongoing requests.
132 | * This implies that disableUntrustedNetwork() should only be invoked when the critical resources have been fetched completely, such as by listening to the[ load event](https://developer.mozilla.org/en-US/docs/Web/API/Window/load_event).
133 | * The API design to handle in-progress requests is currently under discussion.
134 |
135 |
136 | ### Why “untrusted” network
137 |
138 | Certain privacy preserving aggregated reports can be allowed from the fenced frame after disableUntrustedNetwork() has been invoked. After the untrusted network access has been revoked, with the correct permission policies, the fenced frame should be able to invoke APIs like the [Private Aggregation API](https://developer.chrome.com/docs/privacy-sandbox/private-aggregation/#what-is-the-private-aggregation-api) which allows aggregated data to be sent out on the network as that inherently disallows any arbitrary data exfiltration. There may be additional trusted network communications in the future, such as to a secure trusted execution environment.
139 |
140 |
141 | ## Click listener API
142 |
143 | The click listener API is broken into two parts:
144 |
145 | * The embedding context will invoke `addEventListener()` on the `HTMLFencedFrameElement` to listen for a click event on the fenced frame.
146 | * A script inside the fenced frame tree will invoke a new method on `window.fence`, which will trigger the embedding context’s click event listener.
147 |
148 | ### Changes to HTMLFencedFrameElement
149 |
150 | After a fenced frame element object is created, the embedder can call `addEventListener(‘fencedtreeclick’, callback)` on it to attach an event listener to the frame. The new listener can fire when an event with type `click` is fired in the embedded document’s DOM tree. A new [event handler](https://html.spec.whatwg.org/multipage/webappapis.html#event-handler-attributes) named `onfencedtreeclick` will also be exposed on all HTML elements, Document objects, and Window objects, to allow fenced frames' parent elements to listen for `fencedtreeclick` events via the `onfencedtreeclick` attribute as well.
151 |
152 | To start, the spec will only support `fencedtreeclick`, but given that this API relies on the existing DOM event listener specification, it would be trivial to support other `fencedtree*` events in the future.
153 |
154 | The `fencedtreeclick` event listener callback will receive an event object, but it will contain the minimal amount of information necessary to handle the event. Specifically, it will obey the following rules:
155 |
156 | * It will be fired using the base DOM Event constructor, rather than a new event subclass.
157 | * It will be initialized with a type parameter of `'fencedtreeclick'`
158 | * All instances of the event object will be initialized with the same static timestamp value in order to mitigate timing side-channel attacks. The timestamp value of the DOM Event interface is a duration represented by `DOMHighResTimeStamp`,
159 | so user agents can choose a suitable value to use, such as the Unix epoch.
160 | * The event's `isTrusted` field will be true, to indicate that the event is dispatched by the user agent.
161 | * All other attributes of the event object will have the default settings of a newly-constructed event object.
162 | * When the event object is dispatched, its target will always be the `HTMLFencedFrameElement` upon which the event listener was registered.
163 |
164 | Note that specific click information like mouse coordinates are not included. These rules ensure that the event object doesn’t leak information from or about the embedded content.
165 |
166 | ### Changes to window.fence
167 |
168 | Once the embedder has registered the `fencedtreeclick` handler on the fenced frame element, the event needs to be fired while handling the corresponding `click` event within the frame’s content document. This will occur via a new method on the `window.fence` interface, `window.fence.notifyEvent(triggering_event)`. This is different from the existing `reportEvent()` method:
169 |
170 | * `reportEvent()` communicates data about events to a remote URL, and the corresponding beacon also includes data set via `registerAdBeacon` (called by Protected Audience worklets) in the destination URL. See the [Protected Audience API explainer](https://github.com/WICG/turtledove/blob/main/Fenced_Frames_Ads_Reporting.md#reportevent-preregistered-destination-url) for more details.
171 | * `notifyEvent()` communicates that an event occurred to the embedder, and nothing else. No extra information is added to the event. This API call also acts as an opt-in by the fenced frame document’s origin to allow sending the notification to the embedding site.
172 |
173 | The function takes one argument, `triggering_event`, which is a `click` event object that the frame’s content is currently handling. In order to trigger the `fencedtreeclick` event in the embedder, this object’s `isTrusted` field must be true, the event must currently be dispatching, and the event’s type name must be `click`. These requirements guarantee that the `fencedtreeclick` event will only be fired by user-agent-generated click events in response to user actually clicking as opposed to a script-generated event.
174 |
175 | Here's an example of how `window.fence.notifyEvent()` should be used:
176 |
177 | ```javascript
178 | // In the embedder:
179 |
180 | // Make a fenced frame
181 | let fencedframe = ...
182 |
183 | fencedframe.addEventListener('fencedtreeclick', () => {
184 | alert('hello world!');
185 | });
186 |
187 | // In the embedded content:
188 |
189 | document.body.addEventListener('click', (e) => {} {
190 | // Fire a "fencedtreeclick" event at the embedder.
191 | window.fence.notifyEvent(e);
192 | });
193 | ```
194 |
195 | The `notifyEvent()` method will not be available in iframes (same-origin or cross-origin), and will only be available in the fenced frame root’s document.
196 |
197 | ### Click Privacy considerations
198 |
199 | Since this is exfiltrating some information (that a click happened) outside the fenced frame, we will need to consider the following privacy considerations:
200 |
201 | * A possible attack using multiple fenced frames: an embedder creates `n` fenced frames, which all disable network and then determine (by predetermined behavior, or through communication over shared storage) which one of them should display nonempty content. Then if a user clicks on the only nonempty fenced frame, this exfiltrates log(n) bits of information through the click notification. Mitigating this will require some rate limits on the number of fenced frames on a page that are allowed to read from shared storage. This is similar to [shared storage’s existing rate limits](https://github.com/WICG/shared-storage#:~:text=per%2Dsite%20(the%20site%20of%20the%20Shared%20Storage%20worklet)%20budget).
202 | * Click timing could be a channel to exfiltrate shared storage data, but it’s a relatively weak attack since it requires user gesture and is therefore non-deterministic and less accurate. In addition, as a policy based mitigation, shared storage APIs’ invocation will be gated behind [enrollment](https://developer.chrome.com/en/docs/privacy-sandbox/enroll/).
203 | * One potential concern around the `notifyEvent()` API shape is that a single trusted `click` event could be cached by the JS running in the fenced frame and reused in additional `notifyEvent()` calls. However, the requirement that the trusted event *must be dispatching* mitigates this concern. Once the dispatch initiated by the browser completes, `notifyEvent()` will no longer accept the cached event object. If JavaScript on the page then tries to manually re-dispatch the cached event, the object will no longer be trusted (its `isTrusted` field will be set to false).
204 |
205 | ### User Activation
206 |
207 | When a user clicks on a fenced frame, the fenced frame window will have [user activation](https://developer.mozilla.org/en-US/docs/Web/API/UserActivation). User activation is frequently used to [gate access](https://developer.mozilla.org/en-US/docs/Web/Security/User_activation) to powerful Web Platform features unless the user has interacted with the page. In response to a click on a fenced frame, the embedder might want to use one of these features. For example, a personalized third-party payment button is rendered in a fenced frame, and when it’s clicked the first-party merchant site then engages their payment flow. This might involve opening a new window via `window.open()`, or using the `PaymentRequest` API directly, both of which are gated on the transient form of user activation. However, fenced frames don’t automatically propagate user activation to their embedder in the same way that other frames propagate it to their parent. This means that `window.fence.notifyEvent()` has to provide user activation to the embedder manually.
208 |
209 | When `window.fence.notifyEvent(triggering_event)` is called in the fenced frame, three actions will occur related to user activation:
210 |
211 | 1. First, we’ll ensure that the fenced frame currently has *transient* activation. If all of the requirements on `triggering_event` are met (as described [above](#changes-to-windowfence)), this will likely be true already, but it still must be confirmed explicitly.
212 | 2. Transient activation will be consumed in the fenced frame. This is to ensure that only one transient-activation-gated API can be used in response to a single click, and the fenced frame is delegating that one API call to its embedder.
213 | 3. An [activation notification](https://html.spec.whatwg.org/multipage/interaction.html#activation-notification) will be applied in the embedding frame, so that it may call an activation-gated API instead of the fenced frame. The embedding frame will receive both *transient* and *sticky* user activation.
214 |
215 | Transient activation will expire after a set amount of time if it is not consumed by a relevant API (in Chromium, this value is 5 seconds). An embedder document and a fenced frame document could cooperate in an attempt to extend this timeout, by waiting to call `notifyEvent()` until immediately before the expiration time. This would give the embedder an additional few seconds to use an activation-gated API. However, because `notifyEvent()` consumes transient activation in the fenced frame, this still only allows a single transient-activation-gated API call to be made.
216 |
217 | ## Code Example
218 |
219 | Now let's take a look at how shared storage, revocation of untrusted network access, and the click listener API can be combined in a real-world example.
220 |
221 | This example demonstrates how a third-party payment provider (examplepay.com) might embed a personalized payment button onto a merchant's site. First, the payment provider stores card information in shared storage when a user visits their site in a first-party context. Later, a merchant site uses the provider's API to embed a personalized button, which is rendered in a fenced frame. The fenced frame disables untrusted network access, reads the card information from shared storage, and sets up a click handler on the button to initiate the payment flow.
222 |
223 | ```javascript
224 | // When a user navigates to “examplepay.com” and registers their credit card, the last
225 | // four digits of their card are written to Shared Storage.
226 |
227 | // On https://examplepay.com
228 | async function registerCard() {
229 | // Prepare HTTP request with user-provided card infomation.
230 | let request = createCardRegistrationRequest({number: 'XXXX XXXX XXXX 1234',
231 | expDate: 'MM/YY', ...});
232 |
233 | // Register the card information with examplepay.com.
234 | let response = await fetch(request);
235 |
236 | // If the card was registered successfully, write the last 4 digits of the
237 | // card number to Shared Storage for origin "examplepay.com." The data to
238 | // write could come from the response body, a 1p cookie in a response header,
239 | // or somewhere else.
240 | if (response.status === 200) {
241 | let body = await response.json()
242 | await window.sharedStorage.set('last4', body.last4);
243 | console.assert(body.last4 === '1234');
244 | }
245 | }
246 |
247 | // Once the value has been written to Shared Storage, it can later be read inside a
248 | // fenced frame, but only when that frame is same-origin to examplepay.com and has
249 | // its network access restricted. Here’s what that would look like on a merchant page:
250 |
251 | // On merchant page
252 | let example_pay_button = examplePayAPI.createButton();
253 | document.body.appendChild(example_pay_button);
254 |
255 | // In examplePayAPI
256 | function createButton() {
257 | let fenced_frame = document.createElement('fencedframe');
258 | // Create a fenced frame config using a URL directly instead of a config-generating
259 | // API like Protected Audience or sharedStorage.selectURL(). Note that the URL is
260 | // same-origin to the site where the card was first registered.
261 | fenced_frame.config = new FencedFrameConfig('https://examplepay.com/make_button');
262 |
263 | // Registering a "fencedtreeclick" event handler on the fenced frame element allows
264 | // it to respond to a "click" event that fires inside the frame's content.
265 | fenced_frame.addEventListener('fencedtreeclick', () => {
266 | startPaymentFlow();
267 | });
268 | return fenced_frame;
269 | }
270 |
271 | // In the "https://examplepay.com/make_button" fenced frame document
272 | function personalizeButton () {
273 | // By waiting for the page to finish loading, we can ensure that there's
274 | // no additional JS waiting to execute before revoking network.
275 | window.onload = async () => {
276 | // First, disable untrusted network access in the fenced frame.
277 | await window.fence.disableUntrustedNetwork();
278 |
279 | // Read the last four digits of the card from Shared Storage
280 | // and render them in a button.
281 | b = document.createElement('button');
282 | b.textContent = await window.sharedStorage.get('last4');
283 |
284 | // Tell the embedder that the button was clicked, so that the payment flow can be
285 | // initiated. This will fire a "fencedtreeclick" event at the fenced frame element
286 | // in the embedder, which we previously registered a handler for.
287 | b.addEventListener('click', (e) => {
288 | window.fence.notifyEvent(e);
289 | });
290 |
291 | document.body.appendChild(b);
292 | }
293 | }
294 | ```
295 |
296 | ## Try it yourself!
297 |
298 | Development of this feature is ongoing, but the core functionality described above is implemented in Chromium and can be enabled with two command line flags. **We recommend using Chrome Canary to test with our most recent changes.**
299 |
300 | * `--enable-features=FencedFramesDefaultMode,FencedFramesLocalUnpartitionedDataAccess`
301 | * `--disable-features=EnforcePrivacySandboxAttestations`
302 |
303 | We also have some Glitch demo sites for testing the personalized payment button use case.
304 |
305 | * First, visit [demo-payments-provider.glitch.me](https://demo-payments-provider.glitch.me) to register credit card info (please don't use a real credit card number 🙂).
306 | * Then, visit [demo-merchant.glitch.me](https://demo-merchant.glitch.me) for a sample checkout experience with a personalized payment button.
307 |
308 | ## Privacy considerations
309 |
310 | This section goes into the privacy considerations of the 2 states a fenced frame can be in:
311 |
312 | **State 1 (before untrusted network is disabled):** In this state, the fenced frame has contextual information from the embedder site, but not cross-site data. This is equivalent to an iframe and has no privacy concerns.
313 |
314 | **State 2 (after untrusted network is disabled):** In this state, the fenced frame could join the user’s identity on the embedding site with the user’s identity on the fenced frame site, but the join is unable to be exfiltrated out.
315 |
316 | Click privacy considerations are already described in the [earlier section](#click-privacy-considerations).
317 |
318 | An additional element of user privacy is the ability to turn off this feature via a UX control. To achieve this, Chrome will disable this feature when the “Block third-party cookies” settings is enabled. UAs should ensure that users are able to control this capability in alignment with controls for similar cross-site storage capabilities.
319 |
320 | ### New Permissions Policy: fenced-unpartitioned-storage-read
321 |
322 | When a fenced frame accesses unpartitioned data like Shared Storage, a delegation of trust occurs between the embedding context and the fenced frame origin. If the embedder does not trust the content rendered in the fenced frame, it should be able to prevent script in the fenced frame from accessing unpartitioned data. We accomplish this via a new [Permissions Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/Permissions_Policy) directive, which we call `fenced-unpartitioned-storage-read`.
323 |
324 | The `fenced-unpartitioned-storage-read` allowlist will default to `*`, similar to the [`shared-storage` permission](https://wicg.github.io/shared-storage/#permission). If an embedding site wants to restrict a fenced frame’s access to Shared Storage, it can do so by setting a stricter allowlist for that frame, such as `self` or a specific list of origins.
325 |
326 | ## Security considerations
327 |
328 | This new variant of fenced frames (constructed with a normal URL instead of a config or opaque URL) has similar [security considerations](https://github.com/WICG/fenced-frame/blob/master/explainer/README.md#security-considerations) to existing fenced frames but because this variant allows information to flow in from the embedding context to the fenced frame, things like permission delegation are simpler (discussed below).
329 |
330 |
331 | ### Permissions delegation
332 |
333 | Fenced frames constructed using the non-opaque URL constructor do not have to worry about bits of information being passed via permission delegation. Having said that, we cannot allow any permission based feature that would create a channel of communication in the reverse direction, i.e. from the fenced frame to the rest of the page, e.g. Fullscreen since a “fullscreenchange” event that originates from a fenced frame is observable by its embedder. Another factor to consider is not allowing features that are
334 |
335 |
336 |
337 | * either dependent on network access e.g. XMLHttpRequest or,
338 | * other parts not inherently available in fenced frames e.g. Payment feature is dependent on the existence of opener/openee relationship
339 |
340 | Given the above, we would do an audit to see which features are safe to allow in this variant. In the initial launch though, we will likely go with a minimal list of features that we know are necessary for the personalized payment button to work, e.g. shared storage and Aggregate Reporting APIs.
341 |
342 |
343 | ### Process Isolation
344 |
345 | Fenced frames, like Shared Storage worklets, follow Chrome's [Site Isolation](https://www.chromium.org/Home/chromium-security/site-isolation/) model. As Site Isolation improves, so will the security provided to fenced frames.
346 |
347 | ### CSP:frame-ancestors
348 | CSP:frame-ancestors response header only checks up to the fenced frame root in information flows where the embedder’s origin cannot be known inside the fenced frame (e.g., Protected Audience fenced frames). For the information flow in this proposal, since the embedder’s information could be available inside the FF via the src url, it is not a privacy concern to let the CSP:frame-ancestors response header be checked all the way up to the outermost main frame.
349 | With this behavior, the fenced frame can then allowlist origins via CSP:frame-ancestors header that it is ok to be embedded in and exclude others.
350 |
351 | ## Stakeholder Feedback / Opposition
352 |
353 | This change impacts payment providers. We have heard from multiple payment providers about the need for personalized payment buttons, including Google Pay and Shopify. Here’s a [comment on the TAG review](https://github.com/w3ctag/design-reviews/issues/735#issuecomment-1206075921) showing support from Shopify:
354 |
355 | _“As signal boost. Want to note strong interest and support for this on behalf of Shopify. Same/similar [use case and reasons as outlined here](https://developers.googleblog.com/2021/05/updated-google-pay-button-increases-click-through-rates.html).”_
356 |
357 | We have also [heard from TAG reviewers](https://github.com/w3ctag/design-reviews/issues/838#issuecomment-1662399487) about fenced frames’ capability to support non-ads use cases, as given below, and this solution will enable such use cases:
358 |
359 | _“We had a long discussion about how the shape of this, changing the relationship between an iFrame and its embedding page — it must not be unique to the advertising use cases you've listed._
360 |
361 | _We brainstormed along the lines of a site presenting user-generated content in an iFrame, and the payments processes. Have you explored use cases outside the ones you're citing? And if so, what overlaps are you finding?”_
362 |
363 | ## Alternatives considered
364 |
365 | **Alternatives considered: Cookies**
366 |
367 | As 3p cookies are planned to be [phased away](https://privacysandbox.com/open-web/) in Chrome (and in other browsers), there are new APIs that allow 3p cookie access under certain circumstances, e.g. the [requestStorageAccess API](https://groups.google.com/a/chromium.org/g/blink-dev/c/JHf7CWXDZUc).
368 |
369 | If cookies were used for this use case, the proposal will be similar to a variant of requestStorageAccess API being invoked and successfully resolved for an FF only if the FF has its network access disabled.
370 |
371 | **Benefits**
372 |
373 | The benefits of using cookies for this solution are the following:
374 |
375 | * Cookies are well known by developers and well supported by browsers.
376 |
377 | **Downsides**
378 |
379 | * If this solution applies to any 1p cookie then it will have the side effect of the user’s personalized data being sent on the network for every request. To avoid that, there will need to be a new attribute introduced, say “fenced”, so that they are not sent on the network and are only accessible inside a fenced frame. As opposed to the shared storage approach being primarily an API enhancement, this will also require enhancement to the cookies, by adding the “fenced” attribute. The getter code and spec will then need to handle the attribute separately such that it isn’t accessible inside an iframe. Also, the setter should not be accessible inside the FF.
380 | * The cookies being a default network concept does not align well with this change where the “fenced” cookies have to be JS-only.
381 | * Fenced frames by default have a unique and ephemeral partition for cookies and with this change, a given cookie’s value before and after the API resolving will be different, which might lead to confusion. Alternatively, cookie access in the FF before the transition can be blocked.
382 |
383 | **Alternatives considered: Local Storage**
384 |
385 | [Local Storage](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage), like shared storage, is origin scoped and JS-only. Local storage is not an ideal choice for this use case, due to the following reasons:
386 |
387 | * Local storage, like other storage APIs, is being [partitioned](https://privacycg.github.io/storage-partitioning/) in iframes. To allow unpartitioned access, it will rely on a variant of [rSA for storage](https://groups.google.com/a/chromium.org/g/blink-dev/c/SEL7N-xIE5s).
388 | * Fenced frames, by default have a unique and ephemeral partition and either we would disable that access in read-only FFs or there would be a transition where the value of a given data could be different before and after the access is granted, leading to confusion.
389 |
390 | ## References
391 | [TPAC presentation from Sep 2023](https://docs.google.com/presentation/d/1TqtFtK4x3TMd96JEvkbApUaYVdIaUz9uz3wNGPTuqdU/edit?usp=sharing)
392 |
393 | [TPAC presentation from Sep 2024](https://docs.google.com/presentation/d/1xKwCrhN2pD_lxuPfurgjEDkfyAcf3wgxPS9njlG9tT0/edit?usp=sharing)
394 |
395 | [Related Issue](https://github.com/WICG/fenced-frame/issues/15)
396 |
--------------------------------------------------------------------------------
/explainer/generic_payment_button.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WICG/fenced-frame/0fd535135e33ec42dc2cdb1b5fd4e0923d3da565/explainer/generic_payment_button.png
--------------------------------------------------------------------------------
/explainer/integration_with_web_platform.md:
--------------------------------------------------------------------------------
1 | # Integration with the web platform
2 | This document goes into the various ways in which fenced frame interacts with the platform. The web platform is massive and so we expect this document to be a running document with ongoing additions.
3 |
4 | ## Source url
5 | The initial source url for the fenced frame is subjected to various restrictons or no restrictions depending on the use case for the fenced frame. Refer to the use cases document [here](https://github.com/WICG/fenced-frame/blob/master/explainer/use_cases.md) that discusses the source url for each of those.
6 |
7 | ## Origin
8 | The origin of the fenced frame will be the regular origin that it got navigated to. For opaque src, the origin will be that of the url was mapped to by the browser. Any origin-keyed storage and communication channels with other same-origin frames outside the fenced frame tree will be disallowed by using a partitioning key with a nonce. The storage key will use the same nonce for the nested iframes and the root fenced frame, so that same-origin channels can still work within the fenced frame tree. Essentially, along with the storage partitioning effort, the storage and broadcast channel access will be keyed on for the root frame and for a nested iframe. More details related to this can be found [here](https://github.com/WICG/fenced-frame/blob/master/explainer/storage_cookies_network_state.md).
9 |
10 | ## Size
11 | The API that generates a fenced frame config can pick the initial size that the fenced frame document sees, subject to whatever restrictions it deems necessary for its privacy model. If the initial size is fixed, then any changes the embedder makes to the width and height attributes of the will not be reflected inside the fenced frame.
12 |
13 | ## Viewability
14 | Viewability events using APIs like [Intersection Observer](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API) can be a communication channel from the embedding context to the fenced frame. However, to be used as a covert channel, it does require movement of the frame containing the ad on the screen and therefore is hostile to user experience.
15 | Viewability events are important information in the ads workflow for conversion reports and billing; therefore, Intersection Observer will be fully supported in fenced frames and it will be phased out only if/when an alternative mechanism is launched. In the meantime, we plan to add metrics to understand honest use and detect abuse, to help guide the need/development of these eventual mechanisms or mitigations.
16 |
17 | ## Visibility
18 | APIs like [visibilityState](https://developer.mozilla.org/en-US/docs/Web/API/Document/visibilityState) and the corresponding [visibilityChange event](https://developer.mozilla.org/en-US/docs/Web/API/Document/visibilitychange_event) determine the visibility of the tab based on user actions like backgrounding etc. A fenced frame and the main page could join server-side on the basis of when they lose and gain visibility. This kind of joining is also possible with other joining ids like the fenced frame creation time. The network side channel and future mitigations are described more [here](https://github.com/WICG/fenced-frame/blob/master/explainer/network_side_channel.md).
19 |
20 | ## Accessibility
21 | For accessibility, fenced frames will behave like iframes, they will be part of the accessibility tree and a11y users can navigate through its contents with a screen reader.
22 | “title” is an attribute read by screen readers for an iframe and that will continue to be used for fenced frames. A script inside a fenced frame cannot access the title attribute via the same-origin channel window.frameElement.title since frameElement is null for fenced frames.
23 |
24 | ## Interactivity
25 | Fenced frames would have normal user-interactivity as an iframe would.
26 |
27 | ## Focus
28 | Since fenced frames require interactivity, they would need to get system focus. It could be a privacy concern in the following ways:
29 | * A fenced frame and the main page could join server-side on the basis of when one lost focus and another gained focus. The network side channel and future mitigations are described more [here](https://github.com/WICG/fenced-frame/blob/master/explainer/network_side_channel.md)
30 | * For programmatic focus, calling element.focus() might invoke blur() on an element that is in the embedding page thus making a channel from the fenced frame to the embedding page. So the mitigations there would be:
31 | * Only allow programmatic focus()/blur() to be successful if the fenced frame already has system focus.
32 | * Only allow system focus to move to a FF on a user gesture including hitting the tab key and not because of calling focus().
33 |
34 | ## PostMessage
35 | A fenced frame does not allow communication via PostMessage between the embedding and embedded contexts. A fenced frame will be in a separate browsing context group then its embedding page.
36 |
37 | ## Session History
38 | Fenced frames can navigate but their history is not part of the browser back/forward list as that could be a communication channel from the fenced frame to the embedding page. Additionally, fenced frames will always have a replacement-only navigation (back/forward list of length 1) which is a simpler model since it doesn't imply that there's a hidden list of back/forward entries for the nested page, only accessible via history APIs and not via the back/forward buttons, etc. This is also consistent with the iframes new opt-in mode for disjoint session history as discussed in https://github.com/whatwg/html/issues/6501.
39 |
40 | ## Content Security Policy
41 | Fenced frames ineractions with CSP are detailed [here](https://github.com/WICG/fenced-frame/blob/master/explainer/interaction_with_content_security_policy.md).
42 |
43 | ## Permissions and policies
44 | This is discussed in more detail [here](https://github.com/WICG/fenced-frame/blob/master/explainer/permission_document_policies.md).
45 |
46 | ## COOP and COEP
47 | Although COOP is only defined for top-level documents, it has impacts on popups opened by subframes in the page. Because COOP is crucial to the web exposed mitigation against Spectre, Fenced Frames must support COOP in one way or another. Because COOP contains a reporting endpoint, we cannot actually pass the COOP value to the Fenced Frame. So to support COOP, we have two options:
48 | * Forbid Fenced Frames from opening popups entirely.
49 | * Mandate that popups opened from Fenced Frames always have rel no-opener and place them in another BCG (ie the behavior of the strictest form of COOP). We propose fenced frames to take this approach as opening popups is an important functionality that needs to be supported e.g. when a user clicks on an ad in a fenced frame.
50 |
51 | For COEP, If the fenced frame’s embedding page enables COEP then the fenced frame document should allow itself to be embedded as described [here](https://docs.google.com/document/d/1zDlfvfTJ_9e8Jdc8ehuV4zMEu9ySMCiTGMS9y0GU92k/edit#bookmark=id.kaco6v4zwnx2). COEP provides two bits of information to a fenced frame: whether the embedder has COEP enabled, and whether the fenced frame is same-origin with its embedder (through the additional CORP check). We could remove the second one by having COEP always be checked regardless of whether the document in the fenced frame is same origin with its embedder or not. In the initial origin trial, fenced frames behavior will match that of iframes and eventually we will make fenced frames always behave as if it was cross-origin to the embedder.
52 |
53 | ## Opt-in header
54 | Since fenced frames allow a document to have many constraints in place, an opt-in mechanism is a good way for the document to accept those restrictions. The opt-in will make use of the Supports-Loading-Mode proposed [here](https://github.com/WICG/nav-speculation/blob/main/opt-in.md).
55 |
56 | It is also important for sites to opt-in due to security reasons, e.g. csp:frame-ancestors behavior. Frame ancestors checks will stop at the Fenced Frame root for Protected Audience fenced frames. All other fenced frames (e.g., for selectURL or created via FencedFrameConfig) will check all the way up to the primary top-level frame. Protected Audience is different because it is only allowed to have k-anonymous information flow into the fenced frame, and the primary top-level frame’s origin may not be k-anonymous.
57 |
58 | ## Fetch metadata integration
59 | To let a server know that a document is being requested for rendering in a fenced frame, a new Sec-Fetch-Dest HTTP Request Header value of `fencedframe` will be sent in the request.
60 |
61 | ## Unload and beforeunload handlers
62 | Fenced frames will not be supporting unload or before unload handlers. This is because of the following reasons:
63 | * Both of these have the existing issue of unreliability, so hopefully not supporting them should not lead to breaking critical workflows that depend on them.
64 | * There have been issues with both the handlers because running code when the user is trying to navigate away is not very respectful of the user.
65 | * It's a communication channel (the page deletion timestamp). It's not a major one since it's similar to the creation timestamp which is already present but disabling them will eliminate one communication channel between the embedding page and the fenced frame.
66 |
67 | ## Top-level navigation
68 | Some modes of fenced frames allow navigating the top-level frame. The approach adds a new target name called `_unfencedTop` (in the same category as `_self`, `_parent`, `_top`) that works with existing HTML elements/JS APIs (``, ``, `