-
Notifications
You must be signed in to change notification settings - Fork 10
/
Copy pathindex.bs
292 lines (222 loc) · 19.4 KB
/
index.bs
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
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
<pre class="metadata">
Title: requestStorageAccessForOrigin API
Shortname: storage-access-for-origin
Abstract: The requestStorageAccessForOrigin 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/requestStorageAccessForOrigin
Status: CG-DRAFT
Editor: Matt Reichhoff, w3cid 138889, Google https://google.com, [email protected]
Editor: Johann Hofmann, w3cid 120436, Google https://google.com, [email protected]
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: requestStorageAccess; url: dom-document-requeststorageaccess
urlPrefix: https://fetch.spec.whatwg.org/#; spec: fetch
type: dfn
text: http network or cache fetch; url: http-network-or-cache-fetch
</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 requestStorageAccessForOrigin 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/requestStorageAccessForOrigin(origin)}}, 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 requestStorageAccessForOrigin API</h2>
This specification defines a method that can be used to request access to [=unpartitioned data=] on behalf of another [=/origin=] ({{Document/requestStorageAccessForOrigin(origin)}}).
<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/requestStorageAccessForOrigin(origin)}} with {{USVString}} <var ignore>origin</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> requestStorageAccessForOrigin(USVString origin);
};
</pre>
<div algorithm>
When invoked on {{Document}} |doc| with {{USVString}} |requestedOrigin|, the <dfn export method for=Document><code>requestStorageAccessForOrigin(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. If |doc|'s [=node navigable=] is not a [=traversable navigable=], [=reject=] |p| with an "{{InvalidStateError}}" {{DOMException}} and return |p|.
1. If |doc|'s [=Document/origin=] is an [=opaque origin=], [=reject=] |p| with an "{{InvalidStateError}}" {{DOMException}} and return |p|.
1. If |doc|'s [=relevant global object=] is not a [=secure context=], then [=reject=] |p| with a "{{NotAllowedError}}" {{DOMException}} and return |p|.
1. Let |parsedURL| be the the result of running the [=URL parser=] on |requestedOrigin|.
1. If |parsedURL| is failure, [=reject=] |p| with a "{{TypeError}}" {{DOMException}} and return |p|.
1. Let |origin| be |parsedURL|'s [=/origin=].
1. If |origin| is an [=opaque origin=], [=reject=] |p| with an "{{InvalidStateError}}" {{DOMException}} and return |p|.
1. If |doc|'s [=Document/origin=] is [=same origin=] with |origin|, [=resolve=] and return |p|.
1. Let |descriptor| be a newly created {{TopLevelStorageAccessPermissionDescriptor}} with {{PermissionDescriptor/name}} set to "<a permission><code>top-level-storage-access</code></a>" and with {{TopLevelStorageAccessPermissionDescriptor/requestedOrigin}} set to |origin|.
1. Let |has activation| be true if |doc|'s {{Window}} object has [=transient activation=], and false otherwise.
1. Run these steps [=in parallel=]:
1. Let |settings| be |doc|'s [=relevant settings object=].
1. Let |global| be |doc|'s [=relevant global object=].
1. Let |existing state| be |descriptor|'s [=permission state=] with |settings|.
1. If |existing state| is [=permission/granted=]:
1. [=Queue a global task=] on the [=permissions task source=] given |global| to [=resolve=] |p|.
1. Return.
1. If |existing state| is [=permission/denied=]:
1. [=Queue a global task=] on the [=permissions task source=] given |global| to [=reject=] |p| with a "{{NotAllowedError}}" {{DOMException}}.
1. Return.
1. Assert that |doc|'s [=node navigable=] is a [=traversable navigable=].
1. If |has activation| is false:
1. [=Queue a global task=] on the [=permissions task source=] given |global| to [=reject=] |p| with a n "{{InvalidStateError}}" {{DOMException}}.
1. Return.
1. Let |permissionState| be the result of [=requesting permission to use=] "<a permission><code>top-level-storage-access</code></a>" with |descriptor|.
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 `top-level-storage-access`, user agents are known to apply custom rules that will grant or deny a permission without showing a prompt.
1. If |permissionState| is [=permission/granted=]:
1. [=Queue a global task=] on the [=permissions task source=] given |global| to [=resolve=] |p|.
1. Return.
1. If |doc|'s {{Window}} object has [=transient activation=], [=consume user activation=] with it.
1. [=Queue a global task=] on the [=permissions task source=] given |global| to [=reject=] |p| with a "{{NotAllowedError}}" {{DOMException}}.
1. Return |p|.
ISSUE(privacycg/requestStorageAccessForOrigin#15): The permissions task source shouldn't be used directly.
</div>
<h3 id="ua-policies">User Agent top-level storage access policies</h3>
<div algorithm>
To <dfn>determine if a request has top-level storage access</dfn> with [=request=] |request|, run these steps:
1. Let |settings| be |request|'s [=request/client=]'s [=relevant global object=]'s [=relevant settings object=].
1. Let |embedded origin| be |request|'s [=request/url=]'s [=url/origin=].
1. Let |descriptor| be a newly created {{TopLevelStorageAccessPermissionDescriptor}} with {{PermissionDescriptor/name}} set to "<a permission><code>top-level-storage-access</code></a>" and with {{TopLevelStorageAccessPermissionDescriptor/requestedOrigin}} set to |embedded origin|.
1. Let |existing state| be |descriptor|'s [=permission state=] with |settings|.
1. If |existing state| is [=permission/granted=], return true.
1. Return false.
</div>
<h2 id="permissions-integration">Permissions Integration</h2>
The requestStorageAccessForOrigin API defines a [=powerful feature=] identified by the [=powerful feature/name=] "<dfn export permission><code>top-level-storage-access</code></dfn>". It defines the following permission-related algorithms:
<dl>
<dt>{{PermissionDescriptor}}</dt>
<dd>
The "<a permission><code>top-level-storage-access</code></a>" [=powerful feature=] defines a {{PermissionDescriptor}} as follows:
<pre class="idl">
dictionary TopLevelStorageAccessPermissionDescriptor : PermissionDescriptor {
USVString requestedOrigin = "";
};
</pre>
</dd>
<dt>[=powerful feature/permission query algorithm=]</dt>
<dd>
<div algorithm='top-level-storage-access-query'>
To query the "<a permission><code>top-level-storage-access</code></a>" permission, given a {{PermissionDescriptor}} |permissionDesc| and a {{PermissionStatus}} |status|, run the following steps:
1. Set |status|'s {{PermissionStatus/state}} to |permissionDesc|'s [=permission state=].
1. If |status|'s {{PermissionStatus/state}} is [=permission/denied=], set |status|'s {{PermissionStatus/state}} to [=permission/prompt=].
Note: The [=permission/denied=] permission state is not revealed to avoid exposing the user's decision to developers. This is done to prevent retaliation against the user and repeated prompting to the detriment of the user experience.
</div>
</dd>
<dt>[=powerful feature/permission key type=]</dt>
<dd>
A [=permission key=] of the "<a permission><code>top-level-storage-access</code></a>" feature has the type [=site=].
Note: the {{TopLevelStorageAccessPermissionDescriptor/requestedOrigin}} field ensures that the [=permission store entry=] is double-keyed.
</dd>
<dt>[=powerful feature/permission key generation algorithm=]</dt>
<dd>
<div algorithm='top-level-storage-access-key-generation'>
To generate a new [=permission key=] for the "<a permission><code>top-level-storage-access</code></a>" feature, given an [=environment settings object=] |settings|, run the following steps:
1. Let |current origin| be |settings|' [=environment settings object/origin=].
1. If |current origin| is not [=same site=] with |settings|' [=top-level origin=], return null.
1. Return the result of [=obtain a site|obtaining a site=] from |settings|' [=top-level origin=].
Note: the check for whether |settings|' [=environment settings object/origin=] is [=same site=] with |settings|' [=top-level origin=] is intended to disallow permission queries from cross-site frames.
This depends on the invariant that `top-level-storage-access` permission requests are only allowed in a [=top-level browsing context=]. As such, this check is only relevant in {{Permissions/query(permissionDesc)}}.
</div>
</dd>
<dt>[=powerful feature/permission key comparison algorithm=]</dt>
<dd>
<div algorithm='top-level-storage-access-key-comparison'>
To compare [=permission keys=] |key1| and |key2| for the "<a permission><code>top-level-storage-access</code></a>" feature, run the following steps:
1. If |key1| is null or |key2| is null, return false.
1. Return |key1| is [=same site=] with |key2|.
</div>
</dd>
</dl>
<h2 id="fetch-integration">Fetch Integration</h2>
The {{Document/requestStorageAccessForOrigin(origin)}} 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 top-level access| be the result of running [=determine if a request has top-level storage access=] on |request|.
1. If |has top-level 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 [=request/mode=] is "cors" and |request|'s [=request/credentials mode=] is "include", 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="storage-access-api-integration">Storage Access API Integration</h2>
Note: even after a successful {{Document/requestStorageAccessForOrigin(origin)}} call, frames have to explicitly invoke [=requestStorageAccess=] for cookie access.
This modification allows {{Document/requestStorageAccessForOrigin(origin)}} to allow resolution of [=requestStorageAccess=] calls similarly to a prior successful [=requestStorageAccess=] grant.
<div algorithm='storage-access-policy-modification'>
Modify {{Document/requestStorageAccess()}} to insert the following steps before step 13.4 (i.e. before checking transient activation):
1. Let |settings| be <var ignore>doc</var>'s [=relevant settings object=].
1. Let |origin| be |settings|' [=environment settings object/origin=].
1. Let |descriptor| be a newly created {{TopLevelStorageAccessPermissionDescriptor}} with {{PermissionDescriptor/name}} set to "<a permission><code>top-level-storage-access</code></a>" and with {{TopLevelStorageAccessPermissionDescriptor/requestedOrigin}} set to |origin|.
1. If |descriptor|'s [=permission state=] is [=permission/granted=], [=queue a global task=] on the [=permissions task source=] given |global| to [=resolve=] |p|, and return.
1. If |descriptor|'s [=permission state=] is [=permission/denied=], [=queue a global task=] on the [=permissions task source=] given |global| to [=reject=] |p| with a "{{NotAllowedError}}" {{DOMException}}, and return.
</div>
<h2 id="privacy">Privacy considerations</h2>
Like the [[STORAGE-ACCESS]], {{Document/requestStorageAccessForOrigin(origin)}} 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.
[=requestStorageAccess=] requires interaction with an embedded document. By requiring interaction only with the top-level document, {{Document/requestStorageAccessForOrigin(origin)}}
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 [=requestStorageAccess=], the same tension between user consent and prompt fatigue exists with {{Document/requestStorageAccessForOrigin(origin)}}; 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.
Another difference is that queries for the permission can be more sensitive, depending on the context. Note that a frame has to be unable to request the state of either of:
* Whether it was [=permission/granted=] a "<a permission><code>top-level-storage-access</code></a>" permission for some origin while a top-level document.
* Whether arbitrary other origins were [=permission/granted=] the "<a permission><code>top-level-storage-access</code></a>" on the current top-level site.
In the former case, this would allow bogus domains (or combinations thereof) to be used as identifiers; in the latter case, it would reveal state under unrelated origins.
<h2 id="security">Security considerations</h2>
It is important that {{Document/requestStorageAccessForOrigin(origin)}} 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/requestStorageAccessForOrigin(origin)}} 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 [=requestStorageAccess=] is successfully invoked.
For frame access, {{Document/requestStorageAccessForOrigin(origin)}} merely simplifies the activation and prompting requirements.
{{Document/requestStorageAccessForOrigin(origin)}} 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 [=request/mode=] has to be "cors", where reading of the response is blocked unless the embeddee opts-in via sending the appropriate [:access-control-allow-credentials:] header. The sending of the [:origin:] header ensures the embeddee is aware of the embedder's identity.
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.