forked from privacycg/requestStorageAccessFor
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathindex.bs
More file actions
252 lines (187 loc) · 16.2 KB
/
index.bs
File metadata and controls
252 lines (187 loc) · 16.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
<pre class="metadata">
Title: requestStorageAccessFor API
Shortname: storage-access-for-origin
Abstract: The requestStorageAccessFor API allows top-level sites to request access to cross-site cookies on behalf of embedded origins.
Status Text: This specification is intended to be merged into the HTML Living Standard. It is neither a WHATWG Living Standard nor is it on the standards track at W3C.
Text Macro: LICENSE <a href=https://creativecommons.org/licenses/by/4.0/>Creative Commons Attribution 4.0 International License</a>
Group: privacycg
ED: https://github.com/privacycg/requestStorageAccessFor
Status: CG-DRAFT
Editor: Matt Reichhoff, w3cid 138889, Google https://google.com, mreichhoff@google.com
Editor: Johann Hofmann, w3cid 120436, Google https://google.com, johannhof@google.com
Level: None
Markup Shorthands: markdown yes, css no
Complain About: accidental-2119 true
</pre>
<pre class=link-defaults>
spec:html; type:dfn; for:/; text:traversable navigable
spec:html; type:dfn; for:site; text:same site
spec:webidl; type:dfn; text:resolve
spec:fetch; type:dfn; for:/; text:request
</pre>
<pre class="anchors">
urlPrefix: https://tc39.github.io/ecma262/; spec: ECMASCRIPT
text: agent cluster; url: #sec-agent-clusters; type: dfn
urlPrefix: https://w3c.github.io/permissions/; spec: permissions
text: permissions task source; url: #permissions-task-source; type: dfn
urlPrefix: https://privacycg.github.io/storage-access/#; spec: storage-access
type: dfn
text: determine the storage access policy; url: determine-the-storage-access-policy
text: determine if a site has storage access; url: determine-if-a-site-has-storage-access
text: has storage access; url: environment-has-storage-access
text: determine whether the user agent explicitly allows unpartitioned cookie access;url: determine-whether-the-user-agent-explicitly-allows-unpartitioned-cookie-access
urlPrefix: https://fetch.spec.whatwg.org/#; spec: fetch
type: dfn
text: http network or cache fetch; url: http-network-or-cache-fetch
urlPrefix: https://w3c-fedid.github.io/FedCM/; spec: fedcm
type: dfn
text: connected accounts set; url: browser-connected-accounts-set
text: IDP; url: idp
text: RP; url: rp
text: determining the effective FedCM connection status; url: determine-the-fedcm-site-connection-status
urlPrefix: https://privacycg.github.io/storage-access-headers/#; spec: storage-access-headers
type: dfn
text: storage access headers retry check; url: perform-a-storage-access-retry-check
text: storage access status; url : storage-access-status
text: active url: storage-access-status-active
</pre>
<pre class="biblio">
{
"STORAGE-ACCESS": {
"href": "https://privacycg.github.io/storage-access/",
"title": "Storage Access API",
"status": "CG Draft",
"deliveredBy": [
"https://www.w3.org/community/privacycg/"
]
}
}
</pre>
<section class="non-normative">
<h2 id="intro">Introduction</h2>
<em>This section is non-normative.</em>
Many User Agents prevent content from accessing non-[=same site=] data stored in cookies.
This can break embedded content which relies on having access to non-[=same site=] cookies.
The requestStorageAccessFor API enables developers to request access to non-[=same site=] cookies for embedded resources such as iframes, scripts, or images.
It accomplishes this by specifying {{Document/requestStorageAccessFor(requestedOrigin)}}, which allows [=traversable navigable=]s to request access
to unpartitioned cookies on behalf of another [=/origin=].
</section>
<h2 id="infra">Infrastructure</h2>
This specification depends on the Infra standard. [[!INFRA]]
<h2 id="the-rsa-for-api">The requestStorageAccessFor API</h2>
This specification defines a method that can be used to request access to [=unpartitioned data=] on behalf of another [=/origin=] ({{Document/requestStorageAccessFor(requestedOrigin)}}).
<div class=example>
Alex visits `https://social.example/`. The page sets a cookie. This cookie has been set in a [=first-party-site context=].
Later on, Alex visits `https://video.example/`, which has an <{img}> in it which loads `https://social.example/profile-image`. In this case, the `social.example` {{Document}} |doc| is in a [=third party context=], and the cookie set previously might or might not be visible from |doc|`.`{{Document/cookie}}, depending on User Agent storage access policies.
A script on `https://video.example/` could request access on behalf of `https://social.example` by calling |doc|`.`{{Document/requestStorageAccessFor(requestedOrigin)}} with {{USVString}} <var ignore>requestedOrigin</var> as `https://social.example`.
Note: the circumstances for use of the access have to be limited to those cases where the requested origin opts into sharing. More information is available in [[#privacy]] and [[#security]].
</div>
<dfn>Unpartitioned data</dfn> is client-side storage that would be available to a [=site=] were it loaded in a [=first-party-site context=].
A {{Document}} is in a <dfn>first-party-site context</dfn> if it is the [=active document=] of a [=traversable navigable=]. Otherwise, it is in a [=first-party-site context=] if it is an [=active document=] and the [=environment settings object/origin=] and [=top-level origin=] of its [=relevant settings object=] are [=same site=] with one another.
A {{Document}} is in a <dfn>third party context</dfn> if it is not in a [=first-party-site context=].
<h3 id="the-document-object">Changes to {{Document}}</h3>
<pre class="idl">
partial interface Document {
Promise<undefined> requestStorageAccessFor(USVString requestedOrigin);
};
</pre>
<div algorithm>
When invoked on {{Document}} |doc| with {{USVString}} |requestedOrigin|, the <dfn export method for=Document><code>requestStorageAccessFor(requestedOrigin)</code></dfn> method must run these steps:
1. Let |p| be [=a new promise=].
1. If |doc| is not [=Document/fully active=], then [=reject=] |p| with an "{{InvalidStateError}}" {{DOMException}} and return |p|.
1. Let |global| be |doc|'s [=relevant global object=].
1. Let |settings| be |doc|'s [=relevant settings object=].
1. If |global| is not a [=secure context=], then [=/reject=] |p| with a "{{NotAllowedError}}" {{DOMException}} and return |p|.
1. If |doc| is not [=allowed to use=] "`storage-access`", [=/reject=] |p| with a "{{NotAllowedError}}" {{DOMException}} and return |p|.
1. If |doc|'s [=node navigable=] is not a [=traversable navigable=], [=reject=] |p| with an "{{NotAllowedError}}" {{DOMException}} and return |p|.
1. If |doc|'s [=Document/origin=] is an [=opaque origin=], [=reject=] |p| with an "{{NotAllowedError}}" {{DOMException}} and return |p|.
1. If |doc|'s [=active sandboxing flag set=] has its [=sandbox storage access by user activation flag=] set, [=/reject=] |p| with a "{{NotAllowedError}}" {{DOMException}} and return |p|.
1. Let |browsingContext| be |doc|'s [=Document/browsing context=].
1. Let |topLevelOrigin| be the [=top-level origin=] of |doc|'s [=relevant settings object=].
1. Let |topLevelSite| be the result of [=obtain a site|obtaining a site=] from |topLevelOrigin|.
1. Let |parsedURL| be the the result of running the [=URL parser=] on |requestedOrigin|.
1. If |parsedURL| is failure, [=reject=] |p| with a {{TypeError}} and return |p|.
1. Let |embeddedOrigin| be |parsedURL|'s [=/origin=].
1. If |embeddedOrigin| is an [=opaque origin=], [=reject=] |p| with an "{{NotAllowedError}}" {{DOMException}} and return |p|.
1. If |doc|'s [=Document/origin=] is [=same origin=] with |embeddedOrigin|, [=resolve=] and return |p|.
1. Let |has activation| be true if |doc|'s {{Window}} object has [=transient activation=], and false otherwise.
1. Run the following steps [=in parallel=]:
1. Let |process permission state| be an algorithm that, given a [=permission state=] |state|, runs the following steps:
1. [=Queue a global task=] on the [=networking task source=] given |global| to:
1. If |state| is [=permission/granted=]:
1. Set |global|'s [=has storage access=] to true.
1. [=/Resolve=] |p| with {{undefined}}.
1. Else:
1. [=Consume user activation=] given |global|.
1. [=/Reject=] |p| with a "{{NotAllowedError}}" {{DOMException}}.
1. Let |explicitSetting| be the result of [=determine whether the user agent explicitly allows unpartitioned cookie access=] with (|topLevelSite|, |embeddedSite|).
1. If |explicitSetting| is "`disallow`":
1. Run |process permission state| with [=permission/denied=].
1. Abort these steps.
1. If |explicitSetting| is "`allow`":
1. Run |process permission state| with [=permission/granted=].
1. Abort these steps.
1. [=Assert=]: |explicitSetting| is "`none`".
1. If |browsingContext| is a [=top-level browsing context=]:
1. Run |process permission state| with [=permission/granted=].
1. Abort these steps.
1. If |embeddedSite| is [=site/same site=] with |topLevelSite|:
NOTE: This check is [=site/same site=] on purpose, to allow embedded sites to use `requestStorageAccess()` to opt into storage access without involvement from the end user in scenarios where storage access is restricted for security and not privacy purposes.
1. Run |process permission state| with [=permission/granted=].
1. Abort these steps.
1. Let |previous permission state| be the result of [=getting the current permission state=] given "<a permission><code>storage-access</code></a>" and |global|.
1. If |previous permission state| is not [=permission/prompt=]:
1. Run |process permission state| with |previous permission state|.
1. Abort these steps.
1. Let |connected| be the result of [=determining the effective FedCM connection status=] given |topLevelOrigin|, |embeddedOrigin|, |doc|.
1. If |connected|:
NOTE: User agents are encouraged to keep track of which (site, site) tuples have been allowed to access storage due to existing FedCM connections, and double-check that list when accessing cookies to catch malicious attackers that have tricked an [=environment=] into using an incorrect [=has storage access=] bit.
1. Run |process permission state| with [=permission/granted=].
1. Abort these steps.
1. Let |permissionState| be the result of [=requesting permission to use=] "<a permission><code>storage-access</code></a>".
NOTE: Note that when requesting permissions and deciding whether to show a prompt, user agents apply implementation-defined behavior to shape the end user experience. Particularly for `storage-access`, user agents are known to apply custom rules that will grant or deny a permission without showing a prompt.
1. Run |process permission state| with |permissionState|.
1. Return |p|.
NOTE: The intent of this algorithm is to always require user activation before a storage-access permission will be set. Though it is within the means of user agents to set storage-access permissions based on custom heuristics without prior user activation, this specification strongly discourages such behavior, as it could lead to interoperability issues.
ISSUE(privacycg/requestStorageAccessFor#15): The permissions task source shouldn't be used directly.
</div>
<h2 id="permissions-integration">Permissions Integration</h2>
The requestStorageAccessFor API utilizes the existing [=powerful feature=] identified by the [=powerful feature/name=] "<dfn export permission><code>storage-access</code></dfn>" and the integration with permissions is defined in the spec where the permission was defined.
<h2 id="fetch-integration">Fetch Integration</h2>
The {{Document/requestStorageAccessFor(requestedOrigin)}} only directly affects cookie behavior on subresource requests made from top-level documents to the requested [=/origin=].
<div algorithm='cookie-blocking-modification'>
In [=http network or cache fetch=], when determining whether to block cookies, run the following algorithm. A true result means cookies can be unblocked:
1. Let |has storage access| be the result of running [=storage access headers retry check=] on |request|.
1. If |has storage access| is false, return false.
1. Let |is subresource| be true if |request| is a [=subresource request=] and false otherwise.
1. Let |allowed subresource mode| be true if |request|'s [=storage access status=] is "<code>[=active=]</code>", and false otherwise.
1. If |is subresource| is true and |allowed subresource mode| is false, return false.
1. If |request|'s [=request/client=]'s [=relevant global object=]'s [=associated document=] is not a [=traversable navigable=], return false.
1. Return true.
</div>
<h2 id="privacy">Privacy considerations</h2>
Like the [[STORAGE-ACCESS]], {{Document/requestStorageAccessFor(requestedOrigin)}} is intended to enable removal of cross-site cookies. It enables developers to re-gain cross-site cookies with additional constraints.
Note: many of the same considerations as in [[STORAGE-ACCESS#privacy]] apply. This section primarily covers the differences.
{{Document/requestStorageAccess()}} requires interaction with an embedded document. By requiring interaction only with the top-level document, {{Document/requestStorageAccessFor(requestedOrigin)}}
lowers the bar for a potential prompt, though embedded documents can also be quite prominent (or use other techniques to get user interaction).
[=Implementation-defined=] acceptance and rejection steps are intended to allow user agents to reject abusive requests based on logic they see fit.
The prompts used have to be careful to indicate the direction of the request, such that the user is able to understand who is requesting access.
As with {{Document/requestStorageAccess()}}, the same tension between user consent and prompt fatigue exists with {{Document/requestStorageAccessFor(requestedOrigin)}}; much like the Storage Access API,
[=implementation-defined=] acceptance and rejection steps are intended to enable implementers with differing stances on this question to make compromises as they see fit.
<h2 id="security">Security considerations</h2>
It is important that {{Document/requestStorageAccessFor(requestedOrigin)}} not degrade security properties of the web platform, even when compared to post-removal of cross-site cookies.
Third-party cookie removal <a href="https://docs.google.com/document/d/1AsrETl-7XvnZNbG81Zy9BcZfKbqACQYBSrjM3VsIpjY/edit">has potential benefits for security</a>, specifically in mitigating attacks that rely upon authenticated requests, e.g. CSRF.
We do not wish {{Document/requestStorageAccessFor(requestedOrigin)}} to be a foothold for such attacks to leverage.
Note: [[STORAGE-ACCESS#security]] properties hold for much of this proposal. Specifically, frame-level access is only granted once {{Document/requestStorageAccess()}} is successfully invoked.
For frame access, {{Document/requestStorageAccessFor(requestedOrigin)}} merely simplifies the activation and prompting requirements.
{{Document/requestStorageAccessFor(requestedOrigin)}} does expand the scope of concerns in two areas: subresource requests made by the top-level document and potential notification abuse.
<h3 id="subresources">Subresource Requests</h3>
The specific security controls proposed by the API are:
* Any cookies included with the subresource request have to be explicitly marked `SameSite=None`, indicating intent for use in [=third party contexts=].
* For any `SameSite=None` cookies to be included, the request's [=storage access status=] must be "<code>[=active=]</code>".
Additionally, only requests initiated from the top-level document will be eligible for inclusion of `SameSite=None` cookies. This ensures that other embedded frames do not receive escalated privileges.
<h3 id="notification-abuse">Notification Abuse</h3>
Unlike the [[STORAGE-ACCESS]], interaction is only required with the top-level document, rather than an embedded document. This does increase the likelihood of prompting.
Like the Storage Access API, user activation is consumed on denial, which prevents repeated requests.
The [=implementation-defined=] rejection steps also allow for imposition of numeric limits or denylists for abusive actors.
As mentioned in [[#privacy]], because of the direction of the request, the language in user agents' prompts should indicate which site initiated the storage access request.