-
Notifications
You must be signed in to change notification settings - Fork 8
/
index.bs
1427 lines (1092 loc) · 58.5 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
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<!--
-*- vim: set filetype=html:
-*- vim: set colorcolumn=80:
-*- vim: set tw=80:
!-->
<pre class="metadata">
Title: Iframe credentialless
Status: CG-DRAFT
Group: WICG
URL: https://wicg.github.io/anonymous-iframe/
Repository: WICG/anonymous-iframe
Shortname: credentialless-iframe
Level: 1
Editor: Arthur Sonzogni, Google, [email protected]
Editor: Camille Lamy, Google, [email protected]
Abstract:
Iframe credentialless gives developers a way to load documents in third party
iframe using new and ephemeral network/storage/cookies context. In return, the
[Cross-Origin-Embedder-Policy](https://wicg.github.io/cross-origin-embedder-policy/)
(COEP) embedding rules can be lifted.
Developers using COEP can now embed third party iframes that do not.
Indent: 2
Work Status: exploring
Boilerplate: omit conformance
Markup Shorthands: css off, markdown on
WPT Display: inline
WPT Path Prefix: html/credentialless-iframe/
!Tests: <a href=https://github.com/w3c/web-platform-tests/tree/master/html/anonymous-iframe>web-platform-tests html/anonymous-iframe/</a> (<a href=https://github.com/w3c/web-platform-tests/labels/html/anonymous-iframe>ongoing work</a>)
</pre>
<pre boilerplate="copyright">©2022, Google, Inc. All rights reserved.</pre>
<pre class="biblio">
{
"spectre": {
"authors": [
"Paul Kocher",
"Jann Horn",
"Anders Fogh",
"Daniel Genkin",
"Daniel Gruss",
"Werner Haas",
"Mike Hamburg",
"Moritz Lipp",
"Stefan Mangard",
"Thomas Prescher",
"Michael Schwarz",
"Yuval Yarom"
],
"href": "https://spectreattack.com/spectre.pdf",
"title": "Spectre Attacks: Exploiting Speculative Execution"
},
"Cookies": {
"authors": [
"Mike West",
"John Wilander"
],
"title": "Cookies: HTTP State Management Mechanism",
"href": "https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-rfc6265bis-07"
},
"CHIPS": {
"authors": [
"Dylan Cutler",
"Kaustubha Govind"
],
"title": "CHIPS (Cookies Having Independent Partitioned State)",
"href": "https://github.com/WICG/CHIPS"
},
"COEP-require-corp": {
"authors": [
"Mike West"
],
"href": "https://wicg.github.io/cross-origin-embedder-policy/",
"title": "COEP"
},
"COEP-credentialless": {
"authors": [
"Arthur Sonzogni",
"Camille Lamy",
"Ian Clelland",
"Mike West"
],
"href": "https://wicg.github.io/cross-origin-embedder-policy/",
"title": "COEP: credentialless"
},
"WhyCoopCoep": {
"authors": [
"Eiji Kitamura",
"Demenic Denicola"
],
"href": "https://web.dev/why-coop-coep/",
"title": "Why you need \"cross-origin isolated\" for powerful features"
},
"StoragePartition": {
"title": "Client-Side Storage Partitioning",
"href": "https://privacycg.github.io/storage-partitioning/"
},
"STORAGE": {
"title": "Storage",
"href": "https://storage.spec.whatwg.org/"
},
"SecurityPrivacyQuestionnaire": {
"authors": [
"Theresa O’Connor",
"Pete Snyder",
"Jason Novak",
"Lukasz Olejnik",
"Mike West"
],
"title": "Self-Review Questionnaire: Security and Privacy",
"href": "https://www.w3.org/TR/security-privacy-questionnaire/"
}
}
</pre>
<pre class="anchors">
urlPrefix: https://tc39.es/ecma262/; spec: ECMAScript
text: SharedArrayBuffer; type: interface; url: sec-sharedarraybuffer-objects
urlPrefix: https://html.spec.whatwg.org/C/; spec: html
text: BroadcastChannel; type:dfn; url:broadcasting-to-other-browsing-contexts
text: COEP; type: dfn; url: coep
text: COOP; type: dfn; url: cross-origin-opener-policies
text: Cross-Origin-Embedder-Policy; type: dfn; url: coep
text: Cross-Origin-Opener-Policy; type: dfn; url: cross-origin-opener-policies
text: HTMLIframeElement; type:dfn; url:HTMLIframeElement
text: ServiceWorker; type:dfn; url:service-worker-obj
text: SharedWorker; type:dfn; url:sharedworer
text: about:blank; type:dfn; url:about:blank
text: bc-container-document; type: dfn; url: bc-container-document
text: bc-container; type:dfn; url:bc-container
text: check a navigation response's adherence to its embedder policy; type:dfn; url:check-a-navigation-response's-adherence-to-its-embedder-policy
text: compatible with cross-origin isolation; type: dfn; url:compatible-with-cross-origin-isolation
text: concept-document-bc; type:dfn; url:concept-document-bc
text: concept-document-origin; type:dfn; url:concept-document-origin
text: concept-document-policy-container; type: dfn; url: concept-document-policy-container
text: creating a new browsing context; type:dfn; url:creating-a-new-browsing-context
text: crossOriginIsolated; type:dfn; url:concept-settings-object-cross-origin-isolated-capability
text: embedder-policy-report-only-reporting-endpoint; type: dfn; url: embedder-policy-report-only-reporting-endpoint
text: embedder-policy-report-only-value; type: dfn; url: embedder-policy-report-only-value
text: embedder-policy-reporting-endpoint; type: dfn; url: embedder-policy-reporting-endpoint
text: embedder-policy-value; type:dfn; url:embedder-policy-value
text: entry global object; type:dfn; url:entry-global-object
text: hh-replace; type:dfn; url:hh-replace
text: iframe; type:dfn; url:the-iframe-element
text: initialize the document object; type:dfn; url:initialise-the-document-object
text: is initial about:blank; type:dfn; url:is-initial-about:blank
text: navigation params; type:dfn; url:navigation-params
text: navigation-params-hh; type:dfn; url:navigation-params-hh
text: navigation-params-origin; type:dfn; url:navigation-params-origin
text: policy-container-embedder-policy; type: dfn; url: policy-container-embedder-policy
text: queue a cross-origin embedder policy inheritance violation; type:dfn; url: queue-a-cross-origin-embedder-policy-inheritance-violation
text: still on its initial about:blank document; type:dfn; url:still-on-its-initial-about:blank-document
text: window open steps; type:dfn; url:window-open-steps
text: concept-environment-id; type:dfn; url:concept-environment-id
text: concept-environment-targetting-id; type:dfn; url:concept-environment-targetting-id
text: concept-environment-creation-url; type:dfn; url:concept-environment-creation-url
text: concept-environment-target-browsing-context; type:dfn; url:concept-environment-target-browsing-context
text: navigation-params-reserved-environment; type:dfn; url:navigation-params-reserved-environment
text: environment; type:dfn; url:environment
text: concept-request-reserved-client; type:dfn; url:concept-request-reserved-client
text: concept-environment-active-service-worker; type:dfn; url:concept-environment-active-service-worker
text: origin; type:dfn; url:concept-origin
text: set up a window environment settings object; type:dfn; url:set-up-a-window-environment-settings-object
text: script settings for workers; type:dfn; url:script-:settings-for-workers
text: set up a worklet environment settings object; type:dfn; url:set-up-a-worklet-environment-settings-object
text: process a navigate fetch; type:dfn; url:process-a-navigate-fetch
text: active window; type:dfn; url:active-window
text: child browsing context; type:dfn; url:child-browsing-context
<!--text: A; type:dfn; url:A-->
<!--text: A; type:dfn; url:A-->
urlPrefix: https://fetch.spec.whatwg.org/
text: network partition key; type:dfn; url:network-partition-key
text: concept-response; type:dfn; url:concept-response
urlPrefix: https://storage.spec.whatwg.org/
text: storage key; type:dfn; url:storage-key
urlPrefix: https://dom.spec.whatwg.org/
text: EventTarget; type:dfn; url:interface-eventtarget
urlPrefix: https://github.com/w3c/mediacapture-viewport
text: getViewPortMedia; type:dfn; url:/issues/1
urlPrefix: https://www.w3.org/TR/hr-time/
text: high resolution timers; type:dfn; url:/issues/1#issuecomment-812
urlPrefix: https://webidl.spec.whatwg.org/#LegacyUnenumerableNamedProperties
text: LegacyUnenumerableNamedProperties; type:dfn; url:LegacyUnenumerableNamedProperties
urlPrefix: https://tc39.es/ecma262/#sec-execution-contexts
text: javascript execution context; type:dfn; url:sec-execution-contexts
text: back/forward cache; type:dfn; url:https://web.dev/bfcache/
text: threat model; type:dfn; url:/#threat-model
</pre>
<pre class="link-defaults">
spec:fetch; type:dfn; for:/; text:response
spec:html; type:dfn; for:/; text:origin
spec:html; type:dfn; for:Window; text:browsing context
spec:html; type:dfn; for:policy container; text:embedder policy
spec:html; type:dfn; text:environment
spec:url; type:dfn; for:/; text:url
</pre>
<style>
.monkey-patch {
padding: .5em;
border: thin solid #ddd;
border: thin solid 1px;
border-radius: .5em;
margin: .5em calc(-0.5em - 1px);
background-color: rgba(255, 255, 0, 0.03);
backdrop-filter: blur(5px);
box-shadow: 0px 5px 5px 0px rgba(0, 0, 0, 0.05);
}
.brief {
line-height: 10%;
}
.customHighlight {
padding-top:9px ;
padding-bottom:9px ;
background-color: rgba(255,255,0,0.3)
}
</style>
Introduction { #introduction }
============
<em>This section is not normative.</em>
Recommended readings {#recommended-readings}
--------------------
- The [[Spectre]] vulnerability.
- The [[COEP-require-corp]] and [[COEP-credentialless]] headers.
- How and why [=Cross-Origin-Opener-Policy=] ([=COOP=]) and
[=Cross-Origin-Embedder-Policy=] ([=COEP=]) are granting the
[crossOriginIsolated](concept-settings-object-cross-origin-isolated-capability)
capability. See [[WhyCoopCoep]].
A problem {#problem}
=========
<em>This section is not normative.</em>
Deploying [=COEP=] is difficult for some developers, because of third party
iframes. Here is the typical scenario:
1. End users needs performant websites.
2. Some developers get performant websites, by using
multithreading/{{SharedArrayBuffer}} in their top-level document.
3. To mitigate [[Spectre]] attacks, browsers vendors like Chrome, Firefox and
Safari gate {{SharedArrayBuffer}} usage behind the [=crossOriginIsolated=]
capability. This requires deploying both [=COEP=] and [=COOP=]
4. [=COEP=] requirement is recursive: third party iframes are required to
deploy [=COEP=] in order to be embeddable inside a [=COEP=] parent.
5. Waiting for third party to deploy [=COEP=] is painful for developers. This
is often out of their control most of the time.
Beyond performance, there are additionnal features gated behind the
[=crossOriginIsolated=] capability: [=high resolution timers=],
[=getViewportMedia=], etc...
Deploying [=COEP=] is challenging in cases where there's not a single developer
involved, but many. Google Ads, for example, includes third-party content, and
it seems somewhat unlikely that they'll be able to ensure that all the ads
creators will do the work to opt-into being loadable.
Explainer {#explainer}
=========
<em>This section is not normative.</em>
[[COEP-require-corp]] currently tackles data leak attacks by ensuring that
cross-origin resources explicitly opt into being loaded in an environment with
higher risks. This way, servers can protect vulnerable resources by not having
them opt into being loaded in high risk environments.
It would be ideal if we could find an approach that provided robust-enough
protection against accidental cross-process leakage without requiring an
explicit opt-in.
[[COEP-credentialless]] fixed the problem for simple subresources: Instead of
requiring an opt-in from the response, the resource is requested without
credentials. This way, only public resources are potentially leaked to the
attacker. They don't bring any additional value to the attacker.
**Credentialless iframes** are similar, but for `<iframe>`.
Iframes are more difficult to tackle. They not only fetch a resource via a
navigation request, but also create a new context. The new context is able to
fetch data on its own. It can also access data from storage APIs:
[[WebStorage]], [[IndexedDB]], [[web-sql]], [=BroadcastChannel=],
[=SharedWorker=], [=ServiceWorker=], etc
Credentialless iframes is a flag to load documents in iframes, using a new and
ephemeral context. This ensures only a "public" version of the embedded website
can be leaked to the attacker.
What are iframes credentialless? {#proposal-whatis}
--------------------------------
Documents can create an iframe credentialless by adding an `credentialless`
attribute to the iframe tag:
```html
<iframe credentialless src=”https://example.com”></iframe>
```
This property is stored on the iframe. It is also stored and inherited to new
[=Window=] loaded inside e.g.:
<img alt="credentialless inheritance" src="./resources/inheritance.png"></img>
*Credentialless flag inheritance.*
The attribute can be changed programmatically on the `<iframe>`. It will take
effect on new [=Window=] loaded inside. It means the effect will only take place
after an additional navigation.
The state of the credentialless flag is exposed to the [=Window=] through a read-only
constant attribute:
```js
window.credentialless
```
It is true for [=Window=] loaded immediately inside an credentialless iframe, or
deeper below it.
Credentialless iframes and credentials {#proposal-credentials}
---------------------------------
Iframes credentialless cannot use existing credentials and shared storage for
their origin. They are given a blank slate. Unlike sandboxed frames, they can
use storage APIs and register cookies. However, those credentials and storage
can only be shared by documents in credentialless iframes in the page (provided
they meet origin restrictions). They will no longer be accessible once the user
has navigate toward a different top-level document. Essentially, credentialless
iframes are given a temporary [storage
shelf](https://storage.spec.whatwg.org/#storage-shelf) partitioned to
credentialless iframes in the current top-level document.
To achieve this, we rely on modifying the [storage
key](https://storage.spec.whatwg.org/#storage-key) used to access shared storage
by credentialless iframes. As part of the [client-side storage partitioning
effort](https://privacycg.github.io/storage-partitioning/) (declined across
[storage
APIs](https://github.com/wanderview/quota-storage-partitioning/blob/main/explainer.md),
[network
state](https://github.com/MattMenke2/Explainer---Partition-Network-State) and
[Cookie State](https://github.com/DCtheTall/CHIPS)), the storage key of an
environment will no longer be its simple origin as currently described in the
spec. Instead it will be a combination of the origin and the top-level site URL.
In an credentialless iframe, we will replace the top-level site URL in the
partition key by a nonce value, determined once per top-level document. As a
result, the nonce is shared for every credentialless iframe that is a descendant
of the same top-level document. Because the nonce is different every time the
user navigate to a different document, credentialless iframes do not share
storage across different pages, or across cross-document navigations.
<img alt="nonce in partition-key" src="./resources/partition-key.png"></img>
*Storage and credentials are only shared among credentialless iframes, following normal site/origin access checks.*
<img alt="page's nonce" src="./resources/page-change-nonce.png"></img>
*Storage and credentials created by credentialless iframes are no longer
accessible after the top level frame navigated away toward a different top-level
document, because the Storage key for credentialless iframes will be different.
It means when the top-level document is released after a navigation, the storage
used by credentialless iframes can be cleared by the browser, because it will
never be used anymore.
The back-forward cache can keep the top-level document and its credentialless
iframe alive for longer. The nonce assigned to them continue to be used when the
they are restored.
Popups opened by credentialless iframes are not credentialless. However, we
impose that popups opened by credentialless iframes are opened with rel =
noopener set. This is done to prevent OAuth popup flows from being used in
credentialless iframes. See the <a>threat model</a> part for a discussion on why
we impose this restriction.
How do credentialless iframes interact with COEP {#proposal-interactions}
-------------------------------------------
Our proposition is that credentialless iframes are safe enough to embed in a
COEP page, even if they haven’t opted to do so by sending a COEP header. Thus,
when navigating to a document in an credentialless iframe, we do not check
whether it has a COEP and CORP header, even if its parent does not have a COEP
of unsafe-none.
This also means that credentialless iframes can be embedded in cross-origin
isolated pages without documents in them having to deploy COEP.
Credentialless iframes and autofill/password managers {#proposal-autofill}
------------------------------------------------
Browsers that implement autofill or password manager functionalities should make
them unavailable in credentialless iframes. The goal of credentialless iframes
is to preserve storage critical to an iframe function, but to avoid users
logging into credentialless iframes. Autofill and password managers make logging
in easier, and so should be avoided to prevent users accidentally logging in.
This also allows credentialless iframes to have a <a>threat model</a> similar to
a phishing page (see the <a>Threat model</a> part of this explainer below)
Comparison with COEP:credentialless {#proposal-coep-credentialless}
-------------------------------------------------------------------
`COEP:credentialless` and `iframe credentialless` are two proposals sharing the
same goal: helping developers to deploy COEP. Both are based on the idea
public resources are worthless to an attacker. Public data can enter a process
safely without an explicit opt-in from the server. The attacker on the same
process can read the data back using Spectre, but won't get any benefits.
The comparison do not extend further than that. The two features implementations
are very different. One is for simple subresources loaded into the document, the
other is for loading different documents in `<iframe>`.
The name `credentialless` means something very different:
- `COEP:credentialless`: it means loading `no-cors` request without
credentials (e.g. Cookies).
- `Iframe credentialless`: it means mainly associating the document with a
fresh and short lived Cookie/Network/Storage state context.
Alternatives considered {#alternatives}
=======================
<em>This section is not normative.</em>
## Sandboxed iframe ## {#alternatives-sandbox}
Sandboxed iframes without the allow-same-origin flag do not have access to
storage APIs or cookies for their subresource requests. However, the document of
a sandbox iframe can still be requested with credentials, which does not fit the
<a>threat model</a>. We could change sandboxed iframes so that documents are
also requested without credentials.
So why are we proposing introducing a new attribute instead of just using
sandboxed iframes with a new sandbox flag?
First, changing the behavior of sandboxed iframes so that their main resource is
always requested without credentials could break existing websites, as opposed
to introducing a new concept.
Second, we want to minimize the amount of disruption imposed to the content
inside the iframe. Using sandboxed iframes means the iframes cannot use cookies
or storage APIs at all, nor could they access any other frame in the document.
We are worried that this would limit the deployability of the credentialless
solution for opting into crossOriginIsolation. We’re looking to provide
developers with a solution that is as deployable as possible, which is why we’d
rather introduce a new solution that imposes as few restrictions to the iframes
as possible.
We could try to codify these restrictions as a sandbox flag, e.g.
allow-partitioned-storage. This is probably hard to reconcile with the storage
access sandbox flag shipped by Firefox and Safari, especially since a new
sandbox flag would be off by default.
This in turn is another issue with relying on sandboxed iframes for COEP
deployment. Because all flags are off by default, any new flag could impact the
behavior of sandboxed iframes. Not to mention that the syntax is a bit complex
due to the need to add every flag but the allow-same-origin to get all
functionality but access to cookies/storage.
## Opaque origins ## {#alternatives-opaque-origins}
The credentialless iframes model that we propose relies on partitioned storage
(see explainer), using a nonce in the storage key. We have also considered
attributing opaque origins to the credentialless iframes, similar to sandboxed
iframes. This would ensure that the credentialless iframes do not have access to
existing credentials and shared storage since their origin has been changed to
an opaque one.
This solution runs into compatibility issues:
* To allow credentialless iframes to access one another if they are coming from
the same origin we must maintain a mapping of original origin to opaque origin
for each credentialless iframe subtree, which is complex.
* We would probably need to standardize what happens when a frame with an opaque
origin wants to access a storage API since sandboxed iframes with opaque
origins do not have access to storage APIs at all.
* It is not clear how this would interact with other checks pertaining on origin
(e.g. X-Frame-Options, various CSP checks, …) potentially leading to further
breakages.
## Make COEP:credentialless to affect `<iframe>` ## {#alternatives-coep-credentialless}
Originally, `COEP:credentialless` scope was meant to include both simple
subresources like it does today, but also the `<iframe>`. The latter is very
different in kind, because this is not only about the request's credentials,
but also about every storage API usage made later by the document. So it has
been postponed here.
The difficulty is that most of the time, a website will include a mix of:
- Cross-origin `<iframe>` where credentials are important. The URL is
known to the parent, so it can reasonably ask its children website to be
updated so that COEP and CORP headers will be sent. It can wait for the child
to opt-in being embedded.
- Cross-Origin `<iframe>` like ads. The credentials do not really matters, and
the parent do not really control the website being loaded.
So, it is important for the parent to be able to make the decision on a
per-iframe basis. Please note that the decision can never be made directly by
the children, because this affects the navigation's request's credentials. It
would be too late.
With `COEP:credentialless`, site authors can decide to send credentials on a
per-resource basis, by tweaking the `request.mode` and decide in between `cors`
and `no-cors`. One requires the subresource to opt-in being embedded, the other
omit credentials.
We need a similar mechanism for the `<iframe>` element. This became the
`credentialless` attribute as a result.
Tests {#tests}
=====
Status:
[https://wpt.fyi/results/html/credentialless-iframe/](https://wpt.fyi/results/html/credentialless-iframe/)
<!--<wpt>-->
<!--credentialless-iframe-popup.tentative.https.window.js-->
<!--credentialless-window.tentative.https.js-->
<!--cookie-store.tentative.https.window.js-->
<!--cookie.tentative.https.window.js-->
<!--fenced-frame-bypass.tentative.https.window.js-->
<!--fenced-frame.tentative.https.window.js-->
<!--local-storage.tentative.https.window.js-->
<!--require-corp-embed-credentialless-iframe.tentative.https.window.js-->
<!--serviceworker-partitioning.tentative.https.window.js-->
<!--session-storage.tentative.https.window.js-->
<!--sharedworker-partitioning.tentative.https.window.js-->
<!--web-lock.tentative.https.window.js-->
<!--</wpt>-->
Specification {#specification}
=============
Integration with HTML {#spec-html}
---------------------
Note: This corresponds to the following HTML specification change:
[whatwg/html/pull/7695](https://github.com/whatwg/html/pull/7695). <br/>
When merged this section will become obsolete.
### The Iframe attribute ### {#spec-iframe-attribute}
In the [the iframe element](https://html.spec.whatwg.org/#the-iframe-element)
section, define the HTML [=iframe=] <a lt="attr-iframe-credentialless">credentialless</a>
attribute:
<div class="monkey-patch">
The <dfn lt="attr-iframe-credentialless">credentialless</dfn>
attribute, enables loading documents hosted by the <a>iframe</a> with a
new and ephemeral storage partition. It is a boolean value. The default is
false.
</div>
It is exposed to the Javascript [=HTMLIFrameElement=] interface:
<div class="monkey-patch">
<pre class="idl" highlight="idl">
partial interface HTMLIFrameElement {
attribute boolean credentialless;
};
</pre>
<p>The IDL attributes <dfn attribute for="HTMLIFrameElement">credentialless</dfn>,
must <a>reflect</a> the respective content attributes of the same name.</p>
</div>
### The Window attribute ### {#spec-window-attribute}
Add a read-only constant <dfn attribute for="Window">credentialless</dfn> attribute
to the [=Window=] object.
<div class="monkey-patch">
<pre class="idl" highlight="idl">
partial interface Window {
readonly attribute boolean credentialless;
};
</pre>
</div>
### Creating new browsing context ### {#spec-new-browsing-context}
In the <a>creating a new browsing context</a> section:
Add step 5:
<div class="monkey-patch">
5. Let |credentialless| be the result of determining the <a
lt="initial-window-credentialless">initial window credentialless</a> flag, given
|browsingContext|.
</div>
Then later, use it for creating a new [=Window=].
<div class="monkey-patch">
- For the global object, create a new [=Window=] object<span
class="customHighlight">, with {{Window/credentialless}} set to
|credentialless|.</span>
</div>
### Navigating a browsing context ### {#spec-navigating-browsing-context}
In the <a>navigation params</a> <a>struct</a>, adds the credentialless parameter:
<div class="monkey-patch">
<dl>
<dt><dfn lt="navigation-params-credentialless">credentialless</dfn></dt>
<dd>The {{Window/credentialless}} flag to impose on the new
[=Window=]</dd>
</dl>
</div>
------
In the <a>navigate</a> algorithm, adds step 18:
<div class="monkey-patch">
18. Let |credentialless| be the result of computing the <a
lt="navigation-credentialless">navigation's credentialless flag</a>, given
|browsingContext|.</p></li>
</div>
Then later in the same algorithm, use this variable to build the <a>navigation
params</a>.
It is also passed as a new argument to the <a>process a navigate fetch</a>
algorithm, which is also used to create a new <a>navigation params</a>.
------
Then, in the <a>initialize the document object</a> algorithm:
When creating a new [=Window=] in the <a>browsing context</a>, pass the
|credentialless| value.
<div class="monkey-patch">
- For the global object, create a new [=Window=] object<span
class="customHighlight">, with {{Window/credentialless}} set to
|navigationParams|'s <a lt="navigation-params-credentialless">credentialless</a>.</span>
</div>
---
The [=Window=] object must not be reused, when it would lead to keeping an
credentialless flag different from what is in the navigation params.
Example: This is useful in this case:
```js
const iframe = document.body.createElement("iframe");
iframe.credentialless = true;
document.body.appendChild(iframe);
iframe.credentialless = false;
iframe.src = "https://example.test";
// Window for about:blank and for https://example.test must be different.
```
<div class="monkey-patch">
- If |browsingContext| is <a>still on its initial about:blank Document</a>, and
|navigationParams|'s <a lt="navigation-params-hh">history handling</a> is "<a
lt="hh-replace">replace</a>", and |browsingContext|'s <a>active document</a>'s
<a lt="concept-document-origin">origin</a> is <a>same origin-domain</a> with
|navigationParams|'s <a lt="navigation-params-origin">origin</a>, <span
class="customHighlight">and |browsingContext|'s <a>active window</a>'s
{{Window/credentialless}} flag matches |navigationParams|'s <a
lt="navigation-params-credentialless">credentialless</a> flag</span>, then do
nothing.</p>
Note: This means that both the <a lt="is initial about:blank">initial
about:blank</a> [=Document=], and the new [=Document=] that is
about to be created, will share the same [=Window=] object.</p>
</div>
<div>
</div>
### Open popup with noopener ### {#spec-popup-noopener}
In the <a>window open steps</a>, adds step 5:
<div class="monkey-patch">
5. If <a>entry global object</a>'s {{Window/credentialless}} flag is true, then set
<var ignore>noopener</var> to true.
</div>
### General section ### {#spec-section}
Add an "Credentialless iframe" sub-section inside [Loading web
pages](https://html.spec.whatwg.org/C/#browsers) section, in between the [Sandboxing
one](https://html.spec.whatwg.org/C/#sandboxing) and the [Sandboxing
one](https://html.spec.whatwg.org/C/#sandboxing) and the [Cross-origin opener
policies](https://html.spec.whatwg.org/C/#cross-origin-opener-policies) ones:
<div class="monkey-patch">
**7.7 Credentialless iframe**
</div>
<div class="monkey-patch">
Each {{iframe}} element has a mutable
<a lt="attr-iframe-credentialless">credentialless</a> flag attribute.
</div>
<div class="monkey-patch">
Each {{Window}} has a constant {{Window/credentialless}} flag.
An <dfn export>credentialless Window</dfn> is a {{Window}}, whose
{{Window/credentialless}} flag is true.
</div>
<div class="monkey-patch">
To compute the <dfn export lt="initial-window-credentialless">initial window
credentialless flag</dfn>, given a new <a lt="concept-document-bc">browsing
context</a> |browsing context|:
<ol class="brief">
<li><p>Set |embedder| be |browsing context|'s <a
lt="bc-container">container</a>.</p>
<li><p>If |embedder| is not an element, return false.</p></li>
<li><p>Otherwise, set |parentWindow| be the |embedder|'s <a>node
document</a>'s <a>relevant global object</a>.</p></li>
<li><p>Return the union of:</p>
<ul class="brief">
<li><p>|parentWindow|'s {{Window/credentialless}}</p></li>
<li><p>|embedder|'s <a>iframe</a>'s <a
lt="attr-iframe-credentialless">credentialless</a></p></li>
</ul>
</li>
</ol>
</div>
<div class="monkey-patch">
To compute the <dfn export lt="navigation-credentialless">navigation's credentialless flag</dfn>,
given <a lt="concept-document-bc">browsing context</a> |browsing
context|, follows the same steps as in the <a
lt="initial-window-credentialless">initial window credentialless flag</a> algorithm.
</div>
Add several notes in the general section, gathering changes spread elsewhere in
the other algorithms.
<div class="monkey-patch">
Note: New [=Window=]'s {{Window/credentialless}} flag is computed either from the <a
lt="initial-window-credentialless">initial window credentialless flag</a> algorithm for
new <a lt="concept-document-bc">browsing context</a>, or from the <a
lt="navigation-credentialless">navigation's credentialless flag</a> algorithm, executed
when the navigation started, for navigations inside pre-existing <a
lt="concept-document-bc">browsing context</a>.
Note: Popup opened from <a>credentialless Window</a> are always with `noopener` set.
Note: Top-level <a>credentialless Window</a> do not exist.
</div>
### COEP embedder checks ### {#spec-coep-embedder-check}
The [=COEP=] embedding checks can be lifted.
Add a new parameters |credentialless| parameter to the <a>check a navigation
response's adherence to its embedder policy</a> and pass |navigationParams|'s <a
lt="navigation-params-credentialless">credentialless</a>.
<div class="monkey-patch">
<p>To <a>check a navigation response's adherence to its embedder policy</a>
given a <a lt="concept-response">response</a> |response|, a
<a>browsing context</a> |target|, an <a>embedder policy</a>
|responsePolicy|, <span class="customHighlight">and a boolean
|credentialless|</span>:</p>
<ol>
<li><p>If |target| is not a <a>child browsing context</a>, then
return true.</p></li>
<li><p>Let |parentPolicy| be |target|'s <a
lt="bc-container-document">container document</a>'s <a
lt="concept-document-policy-container">policy container</a>'s <a
lt="policy-container-embedder-policy">embedder policy</a>.</p></li>
<li><p>If |parentPolicy|'s <a
lt="embedder-policy-report-only-value">report-only value</a> is <a>compatible
with cross-origin isolation</a> and |responsePolicy|'s <a
lt="embedder-policy-value">value</a> is not, <span
class="customHighlight">and |credentialless| is false</span>, then
<a>queue a cross-origin embedder policy inheritance violation</a> with
|response|, "<code lt="">navigation</code>",
|parentPolicy|'s <a
lt="embedder-policy-report-only-reporting-endpoint">report only reporting
endpoint</a>, "<code lt="">reporting</code>", and |target|'s <a
lt="bc-container-document">container document</a>'s <a>relevant settings
object</a>.</p></li>
<li><p>If |parentPolicy|'s <a lt="embedder-policy-value">value</a>
is not <a>compatible with cross-origin isolation</a> or
|responsePolicy|'s <a lt="embedder-policy-value">value</a> is
<a>compatible with cross-origin isolation</a>, <span
class="customHighlight">or |credentialless| is true, </span>then return
true.</p></li>
<li><p><a>Queue a cross-origin embedder policy inheritance violation</a> with
|response|, "<code lt="">navigation</code>",
|parentPolicy|'s <a
lt="embedder-policy-reporting-endpoint">reporting endpoint</a>, "<code
lt="">enforce</code>", and |target|'s <a
lt="bc-container-document">container document</a>'s <a>relevant settings
object</a>.</p></li>
<li><p>Return false.</p></li>
</ol>
</div>
### Autofill ### {#spec-autofill}
In the "Credentialless iframe" section. Defining the how web browser should configure
their autofilling features.
<div class="monkey-patch">
<p><dfn export>Autofill and credentialless iframe</dfn>: User agents sometimes
have features for helping users fill forms in: for example prefilling the
user's address, password, or payment informations. User agents must disable
those features when the data is both specific to the user and to the
website.
</p>
</div>
### Environment's partition nonce ### {#spec-environment-partition-nonce}
In the "Credentialless iframe" section. Defining the [=page credentialless nonce=].
<div class="monkey-patch">
<p>Each top-level {{Window}} has an associated <dfn export>page credentialless
nonce</dfn>. It is an immutable nonce ("number used once").</p>
</div>
Add the <a for="environment">partition nonce</a> attribute to the
<a>environment</a> object.
<div class="monkey-patch">
<dl>
<dt>A <dfn export for="environment">partition nonce</dfn></dt>
<dd><p>An identifier or null. This is used to discriminate and isolate
environments further. Among others, it is non null for <a>credentialless
Window</a></p></dd>
</dl>
</div>
#### For Navigation #### {#spec-navigation-partition-nonce}
In the <a>process a navigate fetch</a>, add step:
<div class="monkey-patch">
13. If |credentialless| is true, let |partitionNonce| be
|browsingContext|'s <a>top-level browsing context</a>'s <a>page
credentialless nonce</a>, null otherwise.
</div>
|partitionNonce| is used later to create the [=Environment=]. Modify step
13.3.4:
<div class="monkey-patch">
13.3.4. Set <var ignore>request</var>'s <a
lt="concept-request-reserved-client">reserved client</a> to a new
<a>environment</a> whose <a lt="concept-environment-id">id</a> is a unique
opaque string, <a lt="concept-environment-target-browsing-context">target
browsing context</a> is |browsingContext|, <a
lt="concept-environment-creation-url">creation URL</a> is <var
ignore>currentURL</var>, <a>top-level creation URL</a> is
|topLevelCreationURL|, <a>top-level origin</a> is
|topLevelOrigin|, <span class="customHighlight">and <a
for="environment">partition nonce</a> is |partitionNonce|</div>.
</div>
#### For Window #### {#spec-window-partition-nonce}
In the <a>initialize the document object</a>, add step 6.9:
<div class="monkey-patch">
6.9. If |navigationParams|'s <a
lt="navigation-params-credentialless">credentialless</a> is true, let
|partitionNonce| be |browsingContext|'s <a>top-level browsing
context</a>'s <a>page credentialless nonce</a>, null otherwise.
</div>
Then, plumb it to create the [=Environment=] in step 6.10:
<div class="monkey-patch">
6.10 <a>Set up a window environment settings object</a> with |creationURL|,
<var ignore>realm execution context</var>, |navigationParams|'s <a
lt="navigation-params-reserved-environment">reserved environment</a>,
|topLevelCreationURL|, |topLevelOrigin|, <span class="customHighlight">and
|partitionNonce|.</span>
</div>
|partitionNonce| is passed to the <a>set up a window environment settings
object</a> this way:
<div class="monkey-patch">
To <a>set up a window environment settings object</a>, given a <a>URL</a>
|creationURL|, a <a>JavaScript execution context</a> <var ignore>execution
context</var>, null or an <a>environment</a> <var
ignore>reservedEnvironment</var>, a <a>URL</a> |topLevelCreationURL|, an
<a>origin</a> |topLevelOrigin|, and <span class="customHighlight">an identifier
|partitionNonce|</span> run these steps:</p>
</div>
It is used in step 6.
<div class="monkey-patch">
6. Set |settings object|'s <a lt="concept-environment-creation-url">creation
URL</a> to |creationURL|, |settings object|'s <a>top-level creation URL</a> to
|topLevelCreationURL|, |settings object|'s <a>top-level origin</a> to
|topLevelOrigin|, <span class="customHighlight">and |settings object|'s <a
for="environment">partition nonce</a> to |partitionNonce|.</span>
</div>
#### For Worker #### {#spec-worker-partition-nonce}
In the <a>script settings for workers</a> algorithm, add step 8:
<div class="monkey-patch">
8. Set |settings object|'s <a for="environment">partition nonce</a> to <var
ignore>outside settings</var>'s <a for="environment">partition nonce</a>.
</div>
#### For Worklet #### {#spec-worklet-partition-nonce}
In the <a>set up a worklet environment settings object</a> algorithm, modify
step 7:
<div class="monkey-patch">
7. Set <var ignore>settingsObject</var>'s <a lt="concept-environment-id">id</a> to a new
unique opaque string, <a lt="concept-environment-creation-url">creation URL</a> to
<var ignore>inheritedAPIBaseURL</var>, <a>top-level creation URL</a> to null, <a>top-level
origin</a> to <var ignore>outsideSettings</var>'s <a>top-level origin</a>,
<span class="customHighlight"><a for="environment">partition nonce</a> to
<var ignore>outsideSettings</var>'s <a for="environment">partition
nonce</a></span>, <a for="environment">target browsing context</a> to null,
and <a lt="concept-environment-active-service-worker">active service
worker</a> to null.
</div>
Integration with Fetch {#spec-fetch}
----------------------
Note: This corresponds to the following HTML specification change:
[whatwg/fetch/pull/1416](https://github.com/whatwg/fetch/pull/1416). <br/>
When merged this section will become obsolete.
### Plumb the partition-nonce ### {#spec-network-partition-key}
Add the `environment`'s `partition nonce` into the network partition key.
Proceed the following changes:
<div class="monkey-patch">
A <a>network partition key</a> is a tuple consisting of:
- A <a for=/>site</a>.
- null or an <a>implementation-defined</a> value.
- <span class="customHighlight">null or a nonce.</span>
</div>
<div class="monkey-patch">
<p>To <a lt="determine the network partition key">determine the network
partition key</dfn>, given an <a for=/>environment</a> |environment|,
run these steps: