Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update explainer to talk about configs and use cases, rather than urn:uuids and modes #56

Merged
merged 33 commits into from
Feb 23, 2023
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
6f4ae57
Add FencedFrameConfig WebIDL object
gtanzer Nov 10, 2022
0ba427d
Update README.md
gtanzer Nov 10, 2022
63903c2
Update opaque_src.md
gtanzer Nov 14, 2022
fa25fdd
Update opaque_src.md
gtanzer Nov 14, 2022
b745590
Update README.md
gtanzer Nov 14, 2022
d27f6e8
Update README.md
gtanzer Nov 14, 2022
d188c73
Rename opaque_src.md to fenced_frame_config.md
gtanzer Nov 14, 2022
83cf282
Update README.md
gtanzer Nov 14, 2022
21509f0
Update and rename modes.md to use_cases.md
gtanzer Nov 14, 2022
ba59755
Update integration_with_web_platform.md
gtanzer Nov 14, 2022
e209010
Update use_cases.md
gtanzer Nov 14, 2022
89e4669
Update README.md
gtanzer Dec 8, 2022
583dfb5
Update integration_with_web_platform.md
gtanzer Dec 8, 2022
9413947
Update integration_with_web_platform.md
gtanzer Dec 8, 2022
319fcc9
Update interaction_with_content_security_policy.md
gtanzer Dec 8, 2022
c5728f6
Update interaction_with_content_security_policy.md
gtanzer Dec 8, 2022
6b3ce12
Update opaque_ads_use_cases.md
gtanzer Dec 8, 2022
f334f5b
Update permissions_policy_for_API_backed_fenced_frames.md
gtanzer Dec 8, 2022
f39977e
Update use_cases.md
gtanzer Dec 8, 2022
1055c08
Update use_cases.md
gtanzer Dec 8, 2022
f233f19
Update use_cases.md
gtanzer Dec 8, 2022
3d9dc87
Update use_cases.md
gtanzer Dec 8, 2022
f406e08
Address some comments
gtanzer Feb 21, 2023
3883b40
Update size description
gtanzer Feb 21, 2023
aa7f8c1
auction auction
gtanzer Feb 21, 2023
a23153f
Update WebIDL object description
gtanzer Feb 21, 2023
482d34c
enum value -> string
gtanzer Feb 21, 2023
52ef66f
auctionWinnerConfig setter
gtanzer Feb 21, 2023
8ebbf63
Update shivanigithub url
gtanzer Feb 21, 2023
3b6807e
Change heading levels
gtanzer Feb 23, 2023
123e716
Put 'opaque' in quotes
gtanzer Feb 23, 2023
97f6725
Update description of canLoadOpaqueURL
gtanzer Feb 23, 2023
3f09aed
Merge branch 'master' into patch-1
gtanzer Feb 23, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 10 additions & 15 deletions explainer/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ The privacy threat addressed is:

**The ability to correlate the user’s identity/information on the embedding site with that on the embedded site.**

The different use cases and their privacy model are discussed as the different fenced frame modes [here](https://github.com/shivanigithub/fenced-frame/blob/master/explainer/modes.md).
The different use cases and their privacy models are discussed [here](https://github.com/shivanigithub/fenced-frame/blob/master/explainer/use_cases.md).

## Design

Expand All @@ -64,7 +64,7 @@ The idea is that the fenced frame should not have access to both of the followin
* Accessible via an API (e.g., Turtledove) or via access to unpartitioned storage


A primary use case for a fenced frame is to have read-only access to some other partition’s storage, for example, in Turtledove, it is the interest-based ad to be loaded which was added while visiting another site. The URL of the ad is sufficient to give away the interest group that the user belongs to, to the embedding site. Therefore the URL for the ad creative is an opaque url (details [here](https://github.com/shivanigithub/fenced-frame/blob/master/explainer/opaque_src.md)) — which can be used for rendering, but cannot be inspected directly.
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/shivanigithub/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.

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/shivanigithub/fenced-frame/blob/master/explainer/network_side_channel.md).

Expand All @@ -85,15 +85,10 @@ Since fenced frames are embedded frames, they also behave like iframes in many w


```
<fencedframe src="demo_fenced_frame.html"></fencedframe>
var fencedframe = document.createElement('fencedframe');
gtanzer marked this conversation as resolved.
Show resolved Hide resolved
fencedframe.config = FencedFrameConfig({'src': 'demo_fenced_frame.html'});
gtanzer marked this conversation as resolved.
Show resolved Hide resolved
gtanzer marked this conversation as resolved.
Show resolved Hide resolved
```







* 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.
* 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`.

Expand Down Expand Up @@ -135,18 +130,18 @@ This discussion assumes that third-party cookies, like all other third party sto

All fenced frame related functions will live in its own class, in the same way that iframe-related funcionality lives in HTMLIFrameElement.

#### Opaque-Ads Can Load API
#### Can Load API

There are various reasons a fenced frame embedded as an opaque ad 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 will refuse to load. This is a lot for a developer to keep track of.
There are various reasons a fenced frame config 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.

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.

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 create a fenced frame. The API will return a boolean, true if an opaque-ads fenced frame would be able to load in the caller's context, false if not.
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 the config would be able to load in the caller's context, false if not.

##### Example usage

```
HTMLFencedFrameElement.canLoadOpaqueURL();
HTMLFencedFrameElement.canLoadConfig(config);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right now the API is still canLoadOpaqueURL(). Should we make a reference to that, saying that canLoadConfig() will replace it in the future, and in the meantime both will exist?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We discussed maybe removing this section from the explainer for now. Do you still plan on doing that?

Copy link
Collaborator Author

@gtanzer gtanzer Feb 23, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I semi-reverted the changes here. Now it describes the same HTMLFencedFrameElement.canLoadOpaqueURL() function, but as "would a fenced frame config with an opaque mapped url be able to load in this context?"

We can fully replace it when the replacement is fully determined/implemented.

```
```
> true
Expand Down Expand Up @@ -180,11 +175,11 @@ More about security mechanisms are detailed in:
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:


* **Initial size and resize:** To avoid the size attribute being used to communicate user identifying information from the embedding context to the fenced frame, this will be limited to only a few values. E.g. Some of the values that are relevant for ads for the "opaque-ads" mode. We are also considering allowing some of these sizes to be flexible based on the viewport width. Note that since size is a channel, these ads cannot be resized by the publisher.
* **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 makes to the `width` and `height` attributes of the `<fencedframe>` will not be reflected inside the fenced frame.
gtanzer marked this conversation as resolved.
Show resolved Hide resolved
* **IntersectionObserver:** It is important for ads reach and reporting APIs to know the status of the ad frame's visibility, so IntersectionObserver will need to be supported in some way, for instance by only letting it be consumed by browser APIs like [aggregate reporting API](https://github.com/csharrison/aggregate-reporting-api). We can't fully support it as iframes do, to make sure that embedding sites do not (re)position frames such that IntersectionObserver is used for communicating the user’s id to the fenced frame. This is currently under design and intersection observer capability will be supported until the alternative is provided.
* **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. Also delegation of permissions from the embedding context to the fenced frames should not be allowed as that could be a communication channel. This is detailed further [here](https://github.com/shivanigithub/fenced-frame/blob/master/explainer/permission_document_policies.md).
* **Network side channel:** This is detailed more here: [network side channel](https://github.com/shivanigithub/fenced-frame/blob/master/explainer/network_side_channel.md)
* **Navigation url:** Since fenced frames are allowed to open popups or navigate the top-level page in some modes, 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 mitgations going forward (currently being brainstormed). Additionally, this is vulnerable to the network side channel as mentioned above when the embedding site and destnation site are colluding.
* **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 mitgations going forward (currently being brainstormed). Additionally, this is vulnerable to the network side channel as mentioned above when the embedding site and destnation site are colluding.

More of these channels exist and the [integration with web platform](https://github.com/shivanigithub/fenced-frame/blob/master/explainer/integration_with_web_platform.md) details them further.

Expand Down
35 changes: 35 additions & 0 deletions explainer/fenced_frame_config.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Fenced frame configs


# Introduction
gtanzer marked this conversation as resolved.
Show resolved Hide resolved

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 [FLEDGE](https://github.com/WICG/turtledove), the winning ad that's returned from the auction 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.
gtanzer marked this conversation as resolved.
Show resolved Hide resolved


# Proposed solution

`<fencedframe>` has an attribute `config`, rather than `src`. APIs like FLEDGE return a WebIDL object called `FencedFrameConfig`, which 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.
gtanzer marked this conversation as resolved.
Show resolved Hide resolved

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 an enum value `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.
gtanzer marked this conversation as resolved.
Show resolved Hide resolved


## Turtledove Example

When the SSP JS invokes the Turtledove 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.


```
navigator.runAdAuction(myAuctionConfig).then((auctionWinnerConfig) => {
// auctionWinnerConfig value e.g.
// FencedFrameConfig {
// 'src': opaque ('ad.com/foo' internally)
gtanzer marked this conversation as resolved.
Show resolved Hide resolved
// ...
// }
var adFrame = document.createElement('fencedframe');
gtanzer marked this conversation as resolved.
Show resolved Hide resolved
adFrame.setAttribute(config,auctionWinnerConfig);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two issues here:

  1. You have a comma accessor instead of a period
  2. setAttribute() won't actually work here since the config attribute is not a content attribute (string), but only a WebIDL attribute, which are only settable via JS. You can tell this because we don't have [Reflect] in our WebIDL: https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/renderer/core/html/fenced_frame/html_fenced_frame_element.idl;l=10;drc=d99163d64d43b85a9efcf0110a0cfa81886b4616

Copy link

@dmdabbs dmdabbs Feb 9, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The FF Opaque Source explainer example shows the auction result promise resolving to an opaque url...

navigator.runAdAuction(myAuctionConfig).then((auctionWinnerUrl) => {
  // auctionWinnerUrl value e.g.
  // urn:uuid:f81d4fae-7dec-11d0-a765-00a0c91e6bf6
  var adFrame = document.createElement('fencedframe');
  adFrame.setAttribute(src,auctionWinnerUrl);
});

One chooses this path 'upstream' by returning a simple 'render': url from generateBid().

And the means to opt into the new way is to return

'render': {url: renderUrl, size: {width: renderWidth, height: renderHeight}},

and then use

navigator.runAdAuction(myAuctionConfig).then((auctionWinnerConfig) => {
  const adFrame = document.createElement('fencedframe');
  adFrame.config = auctionWinnerConfig;
});

Is that correct?
Or does the embedder set dimensions here...

navigator.runAdAuction(myAuctionConfig).then((auctionWinnerConfig) => {
  const adFrame = document.createElement('fencedframe');
  adFrame.config = new FencedFrameConfig(
     {
        'url': auctionWinnerConfig,
        'width' 300,
        'height': 250
     }  
  );
  
});

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and then use
navigator.runAdAuction(myAuctionConfig).then((auctionWinnerConfig) => { [.....]

Actually the way you'll get FLEDGE's promise to resolve a FencedFrameConfig (instead of a URN) is by having resolveToConfig: true inside the myAuctionConfig (that you pass into runAdAuction()). That's how you communicate your preference to deal in terms of configs (instead of URNs) to FLEDGE. This behavior isn't enabled yet though, so if you do it today you'll still get a URN. In fact, even once the behavior is enabled, it will only be rolled out gradually to more users for testing, so if you pass in resolveToConfig there will be a transition period where FLEDGE may still give you a URN. Long-term, FLEDGE will always resolve to a FencedFrameConfig with or without resolveToConfig: true in the auction configuration.

The reason all of this is not super clear yet is because I have not made the PR to the FLEDGE repository which describes exactly how to take advantage of this, but I'll be doing that soon.


Regarding your first question:

And the means to opt into the new way is to return
'render': {url: renderUrl, size: {width: renderWidth, height: renderHeight}},

I am not sure about this (not as familiar with FLEDGE). @gtanzer do you know?

Copy link
Collaborator Author

@gtanzer gtanzer Feb 9, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, the combination of those things is correct. In order to hook into the new size behavior in FLEDGE, you will need to declare ad sizes in the interest group declaration, make a bid that includes both url and size, put resolveToConfig in the auction config, and then store the winning config in .config as @dmdabbs's first example shows (to the extent that the fenced frames changes are rolled out). (You can't/don't need to modify a config that gets returned from FLEDGE; it already has everything baked in.)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, it may not be necessary to use configs to hook into the FLEDGE size changes: we would just need to do some extra implementation work to make urns work. We'll get back to you on that.

Copy link

@dmdabbs dmdabbs Feb 9, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An IG ad's size metdata may be percentages,

...screen dimension coordinates (e.g. 100sw or 100sh)

The penny hasn't dropped for me how the internal size (I forget your terminology) is set and the external (page-visible dimensions).

Also how pubs/sellers deal with the URN->Newfangled Config rollout/transition.

I appreciate your explanations, @domfarolino and @gtanzer .

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@domfarolino Re the original comment, updated to adFrame.config = auctionWinnerConfig;

});
```

# Backwards compatibility

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.
10 changes: 4 additions & 6 deletions explainer/integration_with_web_platform.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
# Integration with the web platform
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.

## Source attribute
The src attribute which defines the url of the fenced frame is subjected to various restrictons or no restrictions depending on the mode of the fenced frame. Refer the modes details [here](https://github.com/shivanigithub/fenced-frame/blob/master/explainer/modes.md) that discusses the src attribute for each of those.
## Source url
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/shivanigithub/fenced-frame/blob/master/explainer/use_cases.md) that discusses the source url for each of those.

## Origin
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 that the opaque urn 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 <fenced-frame-root-site, fenced-frame-root-origin, nonce> for the root frame and <fenced-frame-root-site, nested-iframe-origin, nonce> for a nested iframe. More details related to this can be found [here](https://github.com/shivanigithub/fenced-frame/blob/master/explainer/storage_cookies_network_state.md).
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 <fenced-frame-root-site, fenced-frame-root-origin, nonce> for the root frame and <fenced-frame-root-site, nested-iframe-origin, nonce> for a nested iframe. More details related to this can be found [here](https://github.com/shivanigithub/fenced-frame/blob/master/explainer/storage_cookies_network_state.md).

## Size
To avoid the size attribute being used to communicate user identifying information from the embedding context to the fenced frame, this will be limited to only a few values. E.g. In the opaque-ads mode, some of the popular values that are (relevant for ads)[https://support.google.com/admanager/answer/1100453?hl=en]. We are also considering allowing some of these sizes to be flexible based on the viewport width on mobile.
Note that since size is a channel, these ads cannot be resized by the publisher.
TODO: The actual sizes that will be used for the "opaque-ads" fenced frame is yet to be determined and published here.
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 <fencedframe> will not be reflected inside the fenced frame.

## Viewability
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. Viewability events are important information in the ads workflow for conversion reports and payments and would be made available in FLEDGE and other ads use cases via separate mechanisms. In the first origin trial of fenced frames, intersection observer will be fully supported and it will be phased out only when the alternative mechanism is launched.
Expand Down
4 changes: 2 additions & 2 deletions explainer/interaction_with_content_security_policy.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ Note that this is not applicable to opaque urls which are discussed in the next

# **Opaque URL**

The first mode of fenced frames is going to be for the [opaque ads use cases](https://github.com/shivanigithub/fenced-frame/blob/master/explainer/modes.md#opaque-ads) and since the url of the fenced frame needs to be hidden from the embedding page, we cannot apply the frame-src directive directly to the mapped url, since CSP reporting can reveal to the embedding page if a fenced frame was successfully created or not. This would lead to the embedding page deriving the possible origins of the opaque ad, going against the isolation guarantees of this mode.
The first use cases for fenced frames are going to be for [opaque ads](https://github.com/shivanigithub/fenced-frame/blob/master/explainer/use_cases.md#opaque-ads) and since the url of the fenced frame needs to be hidden from the embedding page, we cannot apply the frame-src directive directly to the mapped url, since CSP reporting can reveal to the embedding page if a fenced frame was successfully created or not. This would lead to the embedding page deriving the possible origins of the opaque ad, going against the isolation guarantees of this mode.
gtanzer marked this conversation as resolved.
Show resolved Hide resolved

The other option of requiring the embedding site to mention urn:uuid (scheme of the opaque url) in their CSP rules to allow opaque ads, is also not feasible. The limitation is that there are a large number of publisher sites that currently have their csp set to e.g. `frame-src: https:` and requiring the publisher sites to change their headers in order to allow interest-group based advertising to work correctly, is not amenable to deployment.

Expand Down Expand Up @@ -119,7 +119,7 @@ A new CSP directive called fenced-frame-src to be introduced which if not presen
Content-security-policy: fenced-frame-src http://example.com ; frame-src `none`
```

# **Alternatives considered for opaque fenced frames**
# **Alternatives considered for opaque url fenced frames**
* Apply policy to the mapped URL
The alternative approach could have been to apply the csp header to the actual URL and not report it. That would be hard to roll out from a site owner’s perspective. The standard way to add or change a CSP on a site is:
1. Come up with a CSP the developer thinks is right
Expand Down
Loading