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

Proposal: add hasCrossSiteAncestor value to partitionKey in Cookies API #581

Merged
merged 56 commits into from
Sep 12, 2024
Merged
Changes from 34 commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
18ce5e0
Create hasCrossSiteAncestor.md
aselya Mar 28, 2024
deb0dd7
Update hasCrossSiteAncestor.md
aselya Mar 29, 2024
9d579c2
Update hasCrossSiteAncestor.md
aselya Mar 29, 2024
33d24cc
Update hasCrossSiteAncestor.md
aselya Mar 31, 2024
e991178
Update hasCrossSiteAncestor.md
aselya Mar 31, 2024
5ce6b56
Update hasCrossSiteAncestor.md
aselya Apr 1, 2024
04e4a6e
Update proposals/hasCrossSiteAncestor.md
aselya Apr 2, 2024
327ea24
Update proposals/hasCrossSiteAncestor.md
aselya Apr 2, 2024
150ffbf
Update proposals/hasCrossSiteAncestor.md
aselya Apr 2, 2024
0c88b2f
Update proposals/hasCrossSiteAncestor.md
aselya Apr 2, 2024
f064146
Update proposals/hasCrossSiteAncestor.md
aselya Apr 2, 2024
c341071
Update proposals/hasCrossSiteAncestor.md
aselya Apr 2, 2024
769663c
Update hasCrossSiteAncestor.md
aselya Apr 2, 2024
70a3280
Update hasCrossSiteAncestor.md
aselya Apr 2, 2024
ffb23e1
Update hasCrossSiteAncestor.md
aselya Apr 8, 2024
582b880
Update proposals/hasCrossSiteAncestor.md
aselya Apr 8, 2024
b209dd4
Update proposals/hasCrossSiteAncestor.md
aselya Apr 8, 2024
bce3162
Update proposals/hasCrossSiteAncestor.md
aselya Apr 8, 2024
b71a7a7
Update proposals/hasCrossSiteAncestor.md
aselya Apr 8, 2024
1bcf678
Update proposals/hasCrossSiteAncestor.md
aselya Apr 8, 2024
845f868
Update proposals/hasCrossSiteAncestor.md
aselya Apr 9, 2024
b2ef5e7
Update proposals/hasCrossSiteAncestor.md
aselya Apr 9, 2024
ab5eaaa
Update proposals/hasCrossSiteAncestor.md
aselya Apr 9, 2024
cdf260f
Update hasCrossSiteAncestor.md
aselya Apr 9, 2024
c522cdc
Update proposals/hasCrossSiteAncestor.md
aselya Apr 9, 2024
85428ac
Update hasCrossSiteAncestor.md
aselya Apr 11, 2024
86f0e7f
Update hasCrossSiteAncestor.md
aselya May 30, 2024
e244fef
Update hasCrossSiteAncestor.md
aselya May 31, 2024
64a15c5
Update proposals/hasCrossSiteAncestor.md
aselya Jun 26, 2024
cd75fce
Update proposals/hasCrossSiteAncestor.md
aselya Jun 26, 2024
5a3d145
Update hasCrossSiteAncestor.md
aselya Jun 27, 2024
131b666
Update hasCrossSiteAncestor.md
aselya Jul 26, 2024
a831db7
Update proposals/hasCrossSiteAncestor.md
aselya Jul 29, 2024
44a8e80
Update hasCrossSiteAncestor.md
aselya Aug 1, 2024
04a9fee
Update proposals/hasCrossSiteAncestor.md
aselya Aug 5, 2024
3faee04
Update proposals/hasCrossSiteAncestor.md
aselya Aug 5, 2024
a6bf397
Update proposals/hasCrossSiteAncestor.md
aselya Aug 5, 2024
bdf8077
Update hasCrossSiteAncestor.md
aselya Aug 6, 2024
f83b18f
Update proposals/hasCrossSiteAncestor.md
aselya Aug 7, 2024
3a8e2ba
Update hasCrossSiteAncestor.md
aselya Aug 7, 2024
8e05e8d
Update hasCrossSiteAncestor.md
aselya Aug 12, 2024
0d83efa
Update hasCrossSiteAncestor.md
aselya Aug 13, 2024
8328697
Update hasCrossSiteAncestor.md
aselya Aug 16, 2024
9edb236
Update hasCrossSiteAncestor.md
aselya Aug 19, 2024
13ca34f
Update hasCrossSiteAncestor.md
aselya Aug 20, 2024
d76d4a1
Update proposals/hasCrossSiteAncestor.md
aselya Aug 21, 2024
95f4753
Update hasCrossSiteAncestor.md
aselya Aug 21, 2024
0447b92
Update hasCrossSiteAncestor.md
aselya Aug 21, 2024
ac34784
Update proposals/hasCrossSiteAncestor.md
aselya Aug 22, 2024
f21810e
Update hasCrossSiteAncestor.md
aselya Aug 22, 2024
589dab8
Update proposals/hasCrossSiteAncestor.md
aselya Aug 22, 2024
39aefc9
Update hasCrossSiteAncestor.md
aselya Aug 22, 2024
7d42807
Update proposals/hasCrossSiteAncestor.md
aselya Sep 11, 2024
0aed648
Update proposals/hasCrossSiteAncestor.md
aselya Sep 11, 2024
cfe1825
Update proposals/hasCrossSiteAncestor.md
aselya Sep 11, 2024
92b747d
Update hasCrossSiteAncestor.md
aselya Sep 12, 2024
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
122 changes: 122 additions & 0 deletions proposals/hasCrossSiteAncestor.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
# Proposal: Update Extensions to interact with Partitioned Cookies containing Cross-Site Ancestor Chain Bit values

**Summary**

Allow extensions to utilize a hasCrossSiteAncestor boolean value when interacting with partitioned cookies that include a cross-site ancestor chain bit in their partiton key.
aselya marked this conversation as resolved.
Show resolved Hide resolved

#### Background information: Description of Cross-site Ancestor Chain Bit in partitioned cookies

The cross-site ancestor chain bit is a component of the cookie partition key that is set by the browser when keys are created. If the bit indicates true, it means the cookie has been set in a third-party context.
aselya marked this conversation as resolved.
Show resolved Hide resolved

A third-party context occurs when the subresource the cookie is being set on has a cross-site frame ancestor.
Once a frame is considered to be in a third-party context, all requests within that frame and its child frames are also third-party and have a cross site ancestor. Similarly, once a request redirects to a cross-site URL, it is considered third-party (even if it is subsequently redirected back to a first-party request after, that subsequent first-party request is now considered an ABA request).

Note: In the table below, sites A1, A2 and A3 are all first-party to each other.
| Site frame tree |Site cookie is set on| hasCrossSiteAncestor value of cookie| Nodes that can't access the cookie|
|---|---|--|--|
| A1 |A1| false | |
| A1->A2 |A2 | false |
| A1->A2->A3 |A3 | false |
| A1->B |B| true | B|
| A1->B->A2 |A1 | false | B and A2
| A1->B->A2 |B | true | A1 and A2 |
aselya marked this conversation as resolved.
Show resolved Hide resolved
| A1->B->A2 |A2 | true | A1 and B

**Document Metadata**

**Author:** [aselya](https://github.com/aselya)

**Sponsoring Browser:** Chrome

**Contributors:** [DCtheTall](https://github.com/Dcthetall)
aselya marked this conversation as resolved.
Show resolved Hide resolved

**Created:** 2024-04-01

**Related Issues:** [PrivacyCG/CHIPS issue 6 - How do Partitioned cookies interact with browser extensions?](https://github.com/privacycg/CHIPS/issues/6)

## Motivation
https://github.com/privacycg/CHIPS/issues/40 is adding a cross-site ancestor bit value to partitioned cookies.

### Objective
To interact with partitioned cookies containing a cross-site ancestor chain bit correctly, extensions will need to have the ability to to specify a value (hasCrossSiteAncestor) that corresponds to the value of the cross-site ancestor chain bit in partitioned cookies.

#### Use Cases

#### Cookie Manager:
Let's say a cookie manager extension (with host permissions) is used by users to get/set/remove their cookies. As browsers include the cross-site ancestor chain bit in their implementation of partitioned cookies, the extension will need the ability to use the `hasCrossSiteAncestor` parameter to give full insight into the existing cookies and allow the user to set new cookies that include the cross-site ancestor bit correctly.

#### Password Manager:
Let’s say a password manager extension (with host permissions) is used by users to access their login information by setting a cookie that stores their usernames and passwords in an encrypted partitioned cookie. To protect their users against clickjacking, the extension adds a setting that prevents their cookies from being accessed, by default, in embeds that have cross site ancestors without triggering a user prompt. If permission is given through the prompt, the extension sets a cookie with a `hasCrossSiteAncestor` value of true. Upon subsequent visits, the extension checks the cookie store for the presence of a cookie with a `hasCrossSiteAncestor` value of true to determine whether the prompt needs to be rendered.

To allow for this protection and UX flow to work, the extension would need to have the ability to set/get cookies with specific hasCrossSiteAncestor values.

### Known Consumers
All extensions that access and/or modify cookies with awareness of partitioned cookies, through the use of the `partitionKey` property in the `cookies` extension API.

## Specification

### Schema

Adds a new boolean property `hasCrossSiteAncestor` to the `partitionKey` property of the `cookies.Cookie` type.

Adds a new optional boolean property `hasCrossSiteAncestor` to the `partitionKey` property of the details object. Which is the parameter used in the methods `cookies.get()`, `cookies.set()`, `cookies.remove()` and `cookies.getAll()`.

```
// Cookie details object with partitionKey containing hasCrossSiteAncestor value of true.
"details": {
"partitionKey": {
"topLevelSite": "https://example.com",
"hasCrossSiteAncestor": true
}
}
```

### New Permissions
No new permissions are required.

### Manifest File Changes
No new manifest fields are required.

## Security and Privacy
Privacy: The hasCrossSiteAncestor boolean value does not reveal private information.
Copy link
Member

Choose a reason for hiding this comment

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

Privacy risk: "incorrect" use of the cookies API can result in cookies being inadvertently associated with the wrong cookie partition. See my comment at getAll.

Regardless of whether that issue is fixed, we need to call out the risk of inadvertent mixing cookies of cookies from different partitions by extensions that are unaware of partitioning.

Note that it is currently near-impossible for an extension to "correctly" use the cookies API in combination with actual web pages. For the simple use case of "create cookie for this tab" or "remove cookie for this tab" ("this tab" = top-level document), the documented examples simply take the URL of the current tab to feed to the cookies API:

With the introduction of the cross-site ancestor bit, that logic is unreliable. At best it removes/edits the right cookie. It is also probably for it to not remove/modify any cookie. At worst it removes/modifies the wrong cookie.

At the bare minimum, we'd need a way to query the hasCrossSiteAncestor bit for a given tab (tabId), frame (frameId) and/or document (documentId). This can be a dedicated cookies.getHasCrossSiteAncestor({ tabId, frameId, documentId }).

I also considered a generic cookies.getPartitionKey method (which also takes a URL in order to determine whether the cookie would be cross-site or same-site). The advantage of this is that extension developers can then treat the return value as a black box and pass it to the cookies API to always correctly manage the desired cookie, and that the method can account for requestStorageAccess calls in the document if desired.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I agree that it makes sense to provide a way to help developers obtain the expected HasCrossSiteAncestor value. I'd be in support of a cookies.getPartitionKey method and don't mind drafting a proposal for it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added language that describes the preexisting risk, in the privacy risk

Copy link
Member

Choose a reason for hiding this comment

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

Are you going to add getPartitionKey to this proposal or would it be a separate document?

On the one hand it is not strictly necessary for extensions that only use the cookies API to get a view of the partitioned cookies. On the other hand it would be nice if the introduction of the concept of "cross-site ancestor chain bit" also includes a first-class method for extensions to accurately determine the correct bit to use, so they can update their extension and immediately use the right mechanism from the start.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I don't have a preference, is there one the you prefer over the other?

Copy link
Member

Choose a reason for hiding this comment

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

How it works internally in Firefox is explained at https://searchfox.org/mozilla-central/rev/c4966e1c1e6a8cb38c15a27693e8ecd63082055e/toolkit/components/antitracking/StoragePrincipalHelper.h#15

The short version is that a document may initially have access to partitioned storage. Then after granted access through the SSA, it may from then on have access to unpartitioned cookies, and no access to partitioned cookies.

If so, how should we address a case where a cross-site iframe is embedded in a site and cookies.getHasCrossSiteAncestor is called. Then the frame separately is granted SAA which would cause the key to change.

Good question. From the extension API perspective, I would expect "whatever the effective access" is, and that is something that you should raise in the Privacy CG.

Even without SSA, I have already observed that with CHIPS through the Partitioned attribute, that it is possible for the top-level document to have cookies from two partitions. So perhaps we should shape the new getPartitionKey API such that it recognizes that there are multiple partition keys, and return all of them that may potentially appear, with the default one clearly identifiable (e.g. the first in the list, if the return value is a list).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Brought up the question in today's privacy CG meeting, nobody brought up any initial concerns with the designs. However, I think we should ask for additional feedback once the API we finalize the language for the API to make sure.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've given it some more thought and I think that the API we need is cookies.getPartitionKey(), not cookies.getHasCrossSiteAncestor(). By getting the full key, we simplify the flow for the developer, since they will not need to create the key after calling cookies.getHasCrossSiteAncestor(). This also allows the browser to make the decision on what key it prefers for a given frame, if there is more than one valid one. In the event the key provided by the browser is not the one needed by the developer, the developer will have enough information from the initial call to cookies.getPartitionKey() to calculate the key they require.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added language to the PR to describe the API in more detail.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@Rob--W, any concerns with adding this api to the proposal?


Security: The hasCrossSiteAncestor boolean value does not impact security.

### Exposed Sensitive Data
No sensitive data is associated with the cross-site ancestor chain bit.

### Abuse Mitigations
A cookie may only have no cross-site ancestor when the topLevelSite in the partitionKey and the URL with which the cookie is associated with are first-party to each other. To prevent the creation of cookies that violate this, the set method will return an error if the URL and the topLevelSite are not first-party for cookies that are set with no-cross site ancestor.
aselya marked this conversation as resolved.
Show resolved Hide resolved

There are situations that can occur (such as an update to the public suffix list) which can change the if the topLevelSite and the URL are first-party to each other. To accomidate this possibility, the value for `hasCrossSiteAncestor` for the get(), getAll() and remove() methods will not be restricted. This will allow for web extensions to migrate or remove cookies that have become invalid after they have been set.

### Additional Security Considerations
None

## Alternatives
The hasCrossSiteAncestor value could be a value that is not exposed to extensions at all. Browsers that choose to include a cross-site ancestor chain bit in their partitioned cookies, could calculate the expected value of the cross-site ancestor chain bit from the URL associated with the cookie and the topLevelSite in the partitionKey. However, this could cause extensions to be unable to correctly set or get cookies (in an A1->B->A2 situation) as the browser may calculate the incorrect value for hasCrossSiteAncestor since it would not be explicitly provided by the extension.

### Existing Workarounds
To access cookies with the same `topLevelSite` but different `hasCrossSiteAncestor` values (A1->B->A2 context), developers can remove the `Partitioned` attribute from the cookie and use the Storage Access API in the context of web pages.

### Open Web API
The APIs being expanded to include the hasCrossSiteAncestor boolean are specific to extensions.

## Implementation Notes

### Populating hasCrossSiteAncestor when not provided
When no value has been provided for `hasCrossSiteAncestor`, if the `domain` associated with the `cookie` is same-site to the value of the `topLevelSite`, the `hasCrossSiteAncestor` value will be set to false otherwise the value will be set to true.
aselya marked this conversation as resolved.
Show resolved Hide resolved
aselya marked this conversation as resolved.
Show resolved Hide resolved

### APIs affected by the change and the behavior assoicated with the change:

- `cookies.get()`:
If no `hasCrossSiteAncestor` value is provided it will be populated using the algorithim described above. If a `hasCrossSiteAncestor` value is provided without a corresponding `topLevelSite` value, an error will be returned.

- `cookies.getAll()`:
If no value is set for hasCrossSiteAncestor cookies with both true and false values for hasCrossSiteAncestor will be returned. Otherwise, cookies will be returned that match the topLevelSite and the passed value for hasCrossSiteAncestor.
aselya marked this conversation as resolved.
Show resolved Hide resolved

- `cookies.set()`:
As described the Abuse Mitigations section, this method will not allow a hasCrossSiteAncestor value of false, if the URL associated with the cookie and the topLevelSite in the partitionKey are not first-party. If this is attempted, an error will be returned. If a `hasCrossSiteAncestor` value is provided without a corresponding `topLevelSite` value, an error will be returned. Additionally, if no `hasCrossSieAncestor` value is provided it will be populated using the algorithim described above.
aselya marked this conversation as resolved.
Show resolved Hide resolved

- `cookies.remove()`:
If no `hasCrossSieAncestor` value is provided it will be populated using the algorithm described above when determing the cookie to remove. If a `hasCrossSiteAncestor` value is provided without a corresponding `topLevelSite` value, an error will be returned. If `topLevelSite` and `hasCrossSiteAncestor` values are provided, they will be used by the method even if the combination of the values would be invalid.
aselya marked this conversation as resolved.
Show resolved Hide resolved
aselya marked this conversation as resolved.
Show resolved Hide resolved