forked from w3c/webappsec-suborigins
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathindex.bs
891 lines (705 loc) · 42.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
<pre class='metadata'>
Title: Suborigins
Status: ED
ED: https://w3c.github.io/webappsec-suborigins/
Shortname: suborigins
Repository: w3c/webappsec-suborigins
Level: 1
Editor: Joel Weinberger, Google Inc., https://joelweinberger.us, [email protected]
Editor: Devdatta Akhawe, Dropbox Inc., http://devd.me, [email protected]
Abstract:
This specification defines a mechanism for programmatically defining origins
to isolate different applications running in the same physical origin. It
allows a server to specify a namespace on a resource response which is paired
with the scheme/host/port origin tuple. User agents extend the same-origin
policy with this new namespace plus origin tuple to create a security
boundary between this resource and resources in other namespaces.
Group: webappsec
Indent: 2
Markup Shorthands: css off, markdown on
</pre>
<pre class="anchors">
spec: ABNF; urlPrefix: https://tools.ietf.org/html/rfc5234
type: dfn
text: ALPHA; url: appendix-B.1
text: VCHAR; url: appendix-B.1
text: WSP; url: appendix-B.1
type: grammar
text: ALPHA; url: appendix-B.1
text: DIGIT; url: appendix-B.1
text: VCHAR; url: appendix-B.1
text: WSP; url: appendix-B.1
spec: CORS; urlPrefix: https://www.w3.org/TR/cors
type: dfn
text: cross-origin request with preflight; url: #cross-origin-request-with-preflight-0
text: simple cross-origin request; url: #simple-cross-origin-request
spec: Fetch; urlPrefix: https://fetch.spec.whatwg.org
type: dfn
text: credentials; url: credentials
text: credentials mode; url: concept-request-credentials-mode
text: current url; url:#concept-request-current-url
text: fetch; url: concept-fetch
text: request; url: concept-request
text: request origin; url: concept-request-origin
text: response type; url: concept-response-type
text: HTTP-network-or-cache fetch; url: http-network-or-cache-fetch
spec: HTML; urlPrefix: https://html.spec.whatwg.org/multipage/
type: dfn
urlPrefix: comms.html
text: environment settings object; url: #environment-settings-object
text: postMessage; url: #dom-messageport-postmessage
spec: HTML51; urlPrefix: http://www.w3.org/TR/html51/
type: dfn
urlPrefix: dom.html
text: cookie-averse; url: #cookie-averse
text: resource metadata management; url: #resource-metadata-management
spec: HTTP; urlPrefix: https://tools.ietf.org/html/rfc7230
type: grammar
text: OWS; url: section-3.2.3
text: RWS; url: section-3.2.3
spec: URL; urlPrefix: https://url.spec.whatwg.org/
type: dfn
text: scheme; url: #syntax-url-scheme
text: host; url: #syntax-host
text: port; url: #syntax-url-port
spec: XHR; urlPrefix: https://xhr.spec.whatwg.org/
type: dfn
text: request URL; url: #request-url
text: withCredentials; url: #the-withcredentials-attribute
</pre>
<pre class="link-defaults">
spec:dom-ls; type:interface; text:Document
</pre>
<pre class="biblio">
{
"PRIVILEGESEPARATION": {
"title": "Privilege Separation in HTML5 Applications",
"href": "https://www.usenix.org/system/files/conference/usenixsecurity12/sec12-final168.pdf",
"authors": [
"Devdatta Akhawe",
"Prateek Saxena",
"Dawn Song"
],
"publisher": "USENIX"
},
"IFrameSandbox": {
"title": "Play safely in sandboxed IFrames",
"href": "http://www.html5rocks.com/en/tutorials/security/sandboxed-iframes/",
"authors": [
"Mike West"
]
}
}
</pre>
# Introduction # {#intro}
<em>This section is not normative.</em>
Currently, web applications are almost always compartmentalized by using
separate host names to establish separate web origins. This is useful for
helping to prevent XSS and other cross-origin attacks, but has many unintended
consequences. For example, it causes latency due to additional DNS lookups,
removes the ability to use single-origin features (such as the
history.pushState API), and creates cryptic host name changes in the user
experience. Perhaps most importantly, it results in an extremely inflexible
architecture that, once rolled out, cannot be easily and transparently changed
later on.
There are several mechanisms for reducing the attack surface for XSS without
creating separate host-name based origins, but each pose their own problems.
Per-page Suborigins is an attempt to fill some of those gaps. Two of the most
notable mechanisms are Sandboxed IFrames [[IFrameSandbox]] and Content Security
Policy (CSP) [[CSP2]]. Both are powerful but have shortcomings and there are
many external developers building legacy applications that find they cannot use
those tools.
Application developers can use sandboxed frames to completely isolate untrusted content, but they
pose a large problem for containing trusted but potentially buggy code because
it is very difficult, by design, for them to communicate with other frames. The
synthetic origins assigned in a sandboxed frame are random and unpredictable,
making the use of <a>postMessage</a> and <a>CORS</a> difficult. Moreover,
because they are by definition unique origins, with no relationship to the
original origin, designing permissions for them to access resources of the
original origin would be difficult.
Issue: TODO: Problems with CSP sandbox goes well beyond this. synthetic origins
via CSP sandbox do not let you persist state. This makes it impossible to port
an old webpage to run unprivileged. Further, since sandbox inherits on all
iframes within so you can't insert an iframe to say a widget since that gets
sandboxed too and so would need to be rewritten to support such a scenario.
This significantly complicates adoption. This probably needs to go into a new
section This significantly complicates adoption. This probably needs to go into
a new section.
Content Security Policy is also promising but is generally incompatible with
current website design. Many notable companies found it impractical to retrofit
most of their applications with it. On top of this, until all applications
hosted within a single origin are simultaneously put behind CSP, the mechanism
offers limited incremental benefits, which is especially problematic for
companies with large portfolios of disparate products all under the same domain.
## Goals ## {#goals}
* Provide a way for different applications hosted at the same physical origin to
separate their content into separate logical origins. For example,
`https://foobar.com/application` and `https://foobar.com/widget`, today, are,
by definition, in the same origin, even if they are different applications.
Thus an XSS at `https://foobar.com/application` means an XSS at
`https://foobar.com/widget`, even if `https://foobar.com/widget` is
"protected" by a strong Content Security Policy.
* Similarly, provide a way for content authors to split their applications
into logical modules with origin level separation without using different real
origins. Content authors should not have to choose between putting all of
their content in the same origin, on different physical origins, or putting
content in anonymous unique origins (sandboxes).
* Provide safe defaults but also make it as simple as possible to retrofit
legacy applications on the same physical origin with minimal programmatic changes.
This includes providing security-model opt-outs where necessary.
## Use Cases/Examples ## {#usecases}
We see effectively three different use cases for Per-page Suborigins:
1. Separating distinct applications served from the same domain due to
deployment issues but do not need to extensively interact with other
content. Examples include marketing campaigns, simple search UIs, and so on.
2. Enable secure isolation at modular boundaries within a larger web
application by splitting the functional components into different
suborigins. For example, a blogging application might isolate the main blog
viewership module, the admin module, and the authoring module via separate
suborigins.
3. Similar to (2), applications with many users can split information relating
to different users into their own suborigin. For example, a social network
might put each user profile into a unique suborigin so that an XSS within
one profile cannot be used to immediately infect other users or read their
personal messages stored within the account.
<div class="example">
`https://example.com/` runs two applications, Chat and Shopping, used,
respectively, for instant messaging and Internet shopping. The site adminstrator runs
the former at `https://example.com/chat/`, and the latter at
`https://example.com/shopping/`.
The Shopping application has been very well tested and generally does not
contain much untrusted content. In fact, it only takes simple text from
advertisers, and that text only ever appears in HTML contexts, so the
application is able to entity encode the text and stop nearly all cross-site
scripting attacks on the application. Further, the developers have also
enabled a strong Content Security Policy to mitigate potential XSS concerns
on all pages under `https://example.com/shopping/`. The CSP policy only allows scripts
from `scripts.example.com`.
Historically, `https://example.com/chat/` has been riddled with cross-site
scripting attacks. The application takes untrusted content from a wider
variety of sources and for added complexity, that content ends up in many more
contexts, such as HTML tag attributes. On top of that, the developers never
bothered creating a CSP for the application.
This is bad enough, but, unfortunately, it has led to the extremely bad
consequence of attackers using the low hanging fruit of Chat to attack
Shopping, the more desirable target. Cross-site scripting Shopping allows an
attacker to buy goods with the user's account, so this is really the juicy
target.
Since the applications share the same physical origin, these attacks have not
traditionally been that difficult. Once an attacker has executed code on Chat
with an XSS, they open a new window or iframe at `example.com/shopping/`.
Since this is at the same origin as Chat, this allows the attacker to inject
code through the `document` object of the window or iframe into the Shopping
context, allowing the attacker to buy whatever they'd like.
Historical and branding reasons require hosting both applications on the `example.com`
origin. Thus, while these two applications are completely separate, the
company cannot split the products into two different origins (e.g.
`examplechat.com` and `exampleshopping.com`) or different suborigins (e.g.
`chat.example.com` and `shopping.example.com`).
To address this, the developers decide to serve both applications on two
separate suborigins. For all HTTP requests to any subpath of `/chat` or
`/shopping`, example.com includes a header `suborigin: chat` or `suborigin:
shopping`, respectively.
This does not remove any of the XSS attacks on Chat. However, when an attacker
injects code into Chat and opens a window or iframe to
`example.com/shopping/`, they can no longer inject content through the
document as it will fail the same origin check. Of course, the application can
still use `XMLHttpRequest` and `postMessage` to communicate with the document,
but that will only be through well defined APIs. In short, the CSP of the
Shopping application is now actually effective as the permissive Chat
application is no longer a bypass of it.
</div>
Issue: TODO: We probably should add additional examples, or perhaps match an
example to each bullet above.
# Key Concepts and Terminology # {#terms}
Issue: TODO(jww) This needs to be filled in once we have a pretty good handle on
the basic structure of this document. At that point, we should extract the terms
defined throughout the spec and place them here.
This section defines several terms used throughout the document.
The terms <dfn>origin</dfn>, <dfn>cross-origin</dfn>, and <dfn>same-origin</dfn>
are defined by the Origin specification. [[!RFC6454]]
<dfn>CORS</dfn>, or <dfn>Cross-Origin Resource Sharing</dfn>, are defined by the
CORS specification. [[!CORS]]
<dfn>XMLHttpRequest</dfn>, or <dfn>XHR</dfn>, is defined by the XMLHttpRequest
specification. [[!XHR]]
The term <dfn>cross-site scripting</dfn>, or <dfn>XSS</dfn> for short, refers to
a content injection attack where an attacker is able to execute malicious code
in a victim origin. See the <a
href="https://www.owasp.org/index.php/Cross-site_Scripting_(XSS)">OWASP page on
Cross-site Scripting</a> for more information.
## Grammatical Concepts ## {#grammar}
The Augmented Backus-Naur Form (ABNF) notation used in this document is
specified in RFC5234. [[!RFC5234]]
Lowercase characters, the `a-z` portion of <a>ALPHA</a>, are defined by the grammar:
<pre dfn-type="grammar" link-type="grammar">
<dfn>LOWERALPHA</dfn> = %x61-7A ; a-z
</pre>
# Defining a Suborigin # {#defining-suborigin}
Origins are a mechanism for user agents to group URIs into protection domains.
As defined in [[!RFC6454]], two URIs are in the <a>same-origin</a> if they share
the same <a>scheme</a>, <a>host</a>, and <a>port</a>. If URIs are same-origin,
then they share the same authority and can access all of each others resources.
Compared to per-user isolation in traditional operating systems, the
same-origin policy isolates distinct applications identified by their origins.
This has been a successful isolation mechanism on the Web. However, it does
limit the flexibility of a page to separate itself into a new protection domain
as it automatically shares authority with all other identical origins. These origins
are defined by physical, rather than programmatic, properties. While it is
possible to setup unique domains and ports for different parts of the same
application (scheme is more difficult to separate out), there are a diverse set
of practical problems in doing so.
Suborigins provide a mechanism for creating this type of separation
programatically. Any resources may provide, in a manner detailed below, a string
value <a>suborigin namespace</a>. If either of two URIs provide a suborigin
namespace, then the two URIs are in the <a>same-origin</a> if and only if they
share the same <a>scheme</a>, <a>host</a>, <a>port</a>, and <a>suborigin
namespace</a>.
Q. In today's Web, can't a site get the effective same protection domain simply
by hosting their content at different subdomains?
A. Yes, but there are many practical reasons why this is difficult:
## Difficulties using subdomains ## {#difficulties}
### Separate applications, same origin ### {#separate-applications-same-origin}
Google runs Search and Maps on the same domain, respectively
`https://www.google.com` and
`https://www.google.com/maps`. While these two applications are
fundamentally separate, there are many reasons for hosting them on the same
origin, including historical links, branding, and performance. However, from
security perspective, this means that a compromise of one application is a
compromise of the other since the only security boundary in the browser is the
origin, and both applications run in the same origin. Thus, even if
Google Search were to successful implement a strong Content Security Policy
[[CSP2]], if Google Maps were to have an XSS vulnerability, it would be
equivalent to having an XSS on Google Search as well, negating Google Search's
security measures.
### Separation within a single application ### {#separation-in-single-application}
Separation is sometimes desirable within a single application because of the
presence of untrusted data. Take, for example, a social networking site with
many different user profiles. Each profile contains lots of untrusted content
created by a single user but it's all hosted on a single origin. In order to
separate untrusted content, the application might want a way to put all profile
information into separate logical origins while all being hosted at the same
physical origin. Furthermore, all content within a profile should be able to
access all other content within the same origin, even if displayed in unique
frames.
This type of privilege separation within an application has been shown to be
valuable and reasonable for applications to do by work such as
Privilege Separation in HTML5 Applications by Akhawe et al
[[PRIVILEGESEPARATION]]. However, these systems rely on cross frame messaging
using `postMessage` even for content in the same trust boundary since
they utilize `sandbox`. This provides much of the motivation for the
named container nature of suborigins.
## Threat Model ## {#threat-model}
<a>Origins</a> and the <a
href="http://www.w3.org/Security/wiki/Same_Origin_Policy">Same-Origin Policy</a>
have provided a strong defense against
malicious applications. Instead of giving the application the power of the user,
applications on the Web are limited to a unique space that is defined by their
host. However, by tying the origin to the physical host, this has limited the
power of developers.
Suborigins attempt to provide developers with tool to contain two different
principles that are on the same host. Suborigins allow two or more applications
or modules to be hosted at the same origin but use the same origin policy to
separate them from each other.
### Cross-Document Attacker ### {#threat-model-cross-doc}
An attacker that is able to compromise one document should not be able to
control another document that is on the same host but delivered in a different
suborigin namespace. If an attacker is able to <a>XSS</a>, for example, a
document on
`example.com` delivered in the suborigin namespace `foo`,
the attacker should not be able to control any document on
`example.com` not in the `foo` namespace.
Issue: TODO(devd): Should we also assert that attacker on main/parent origin
cannot compromise the app in sub-origin?
### Out of Scope Attacker ### {#threat-model-out-of-scope}
This tool is purely for modularity and meant to be an application security tool.
It is <em>not</em> meant to help users differentiate between two different
applications at the same host, as reflected by the fact that user agents may not
put the suborigin in user-visible UI. Additionally, suborigins cannot protect
against colluding malicious or compromised applications.
## Relationship of Suborigins to Origins ## {#suborigins-vs-origins}
Suborigins, in fact, do not provide any new authority to resources. Suborigins
simply provide <em>an additional way to construct Origins</em>. That is,
Suborigins do not supercede Origins or provide any additional authority above
Origins. From the user agent's perspective, two resources in different
Suborigins are simply in different Origins, and the relationship between the two
resources should be the same as any other two differing origins as described in
[[!RFC6454]]. However, given the impracticalities this may impart on some
applications who might want to adopt Suborigins, a few security-model opt-outs
to ease the use of Suborigins in legacy applications are also presented. See
[[#security-model-opt-outs]] for more information.
Issue: TODO: Figure out if this all will require updates to the HTML spec's
origin definition.
## Representation of Suborigins ## {#representation}
At an abstract level, a suborigin consists of the <dfn>physical origin</dfn>,
which is a <a>scheme</a>, <a>host</a>, and <a>port</a>, plus a <a>suborigin
namespace</a>. However, as mentioned above, suborigins are intended to fit
within the framework of [[!RFC6454]]. Therefore, this specification provides a
way of serializing a Suborigin bound resource into a physical origin. This is
done by inserting the suborigin namespace into the host of the Origin, thus
creating a new host but maintaining all of the information about both the
original scheme, host, port, and the suborigin namespace. The serialization
format appends the string "-so" to the scheme and prepends the host name with
the suborigin namespace followed by a "." character.
For example, a resource hosted at `https://example.com/` in
the suborigin namespace `profile` would be serialized as
`https-so://profile.example.com/`.
Similarly, a resource hosted at `https://example.com:8080/` in
the suborigin namespace `separate` would be serialized as
`https-so://separate.example.com:8080/`.
Internally, the user agent just tracks the <a>suborigin namespace</a> of the resource.
When the origin needs to be serialized, the user agent should
follow the algorithm in [[#serializing]].
Note: The underscore character is not a valid character for hostnames used
on the web [[!RFC1123]] and as a result, the serialization above cannot collide
with valid existing hostnames.
Issue: TODO: Determine how the serialization should relate to the URL spec:
https://url.spec.whatwg.org/#host-parsing
## Opting into a Suborigin ## {#opting-in}
Unlike the `sandbox` attribute, suborigin namespaces are predictable and
controllable. Because of this, potentially untrusted content cannot opt into
suborigins, unlike iframe sandboxes. If they could, then an XSS on a site could
enter a specific suborigin and access all of its resources, thus violating the
isolation suborigins intend to provide. To prevent this, the
server (rather than a resource itself) is the only authoritative
source of the suborigin namespace of a resource. The server communicates the
suborigin of a resource to the user agent through a new `suborigin` header,
which takes a string value that is the namespace. For example, to put a
resource in the `testing` suborigin namespace, the server would specify the
following HTTP header in the response:
<pre>
suborigin: testing
</pre>
## The `suborigin` header ## {#the-suborigin-header}
Suborigins are defined by a <dfn>suborigin</dfn> HTTP response header. The syntax
for the name and value of the header are described by the following ABNF
grammar [[!RFC5234]]:
<pre dfn-type="grammar" link-type="grammar">
<dfn>suborigin-name</dfn> = 1*( <a>LOWERALPHA</a> / <a>DIGIT</a> / "-" )
<dfn>suborigin-policy-option</dfn> = "'unsafe-postmessage-send'"
/ "'unsafe-postmessage-receive'"
/ "'unsafe-cookies'"
/ "'unsafe-credentials'"
<dfn>suborigin-policy-list</dfn> = 1*(<a>RWS</a> <a>suborigin-policy-option</a> <a>OWS</a>)
<dfn>suborigin-header</dfn> = <a>suborigin-name</a> [ <a>suborigin-policy-list</a> ]
</pre>
User agents MUST ignore multiple suborigin headers and only apply the first.
A resource's <dfn>suborigin namespace</dfn> is the value of the
<a link-type="grammar">suborigin-name</a> in the `suborigin` header.
A resource's <dfn>suborigin policy</dfn> is the list of individual
<a link-type="grammar">suborigin-policy-option</a> values in the `suborigin`
header's <a link-type="grammar">suborigin-policy-list</a>.
## Accessing the Suborigin in JavaScript ## {#suborigin-in-js}
A `suborigin` property is added to the <a>document</a> object which
<a>reflects</a> the value of the suborigin namespace for the current execution
context. If there is no suborigin namespace, the value should be undefined.
Additionally, the `origin` property of the <a>document</a> object should reflect
the serialized value of the origin as returned by [[#serializing]].
Issue: TODO(jww): Need to write the formal IDL for this.
# Access Control # {#access-control}
Cross-origin (including cross-suborigin) communication is tricky when suborigins
are involved because they need to be backwards compatible with user agents that
do not support suborigins while providing origin-separation for user agents that
do support suborigins. The following discussions discuss the three major
cross-origin mechanisms that are relevant: <a>CORS</a>, <a>`postMessage`</a>,
and workers [[!WORKERS]].
Issue: TODO(devd): Making things specific to XHR or CORS is weird. We should
just make all fetches inside a suborigin CORS fetches and be done with it.
## CORS ## {#cors-ac}
For pages in a suborigin namespace, all <a>`XMLHttpRequest`</a>s and
<a>`fetch`</a> requests to any URL should be treated as cross-origin, thus
triggering a <a>cross-origin request with preflight</a> for all non-<a>simple
cross-origin requests</a>. Additionally, all requests from a suborigin namespace
must include a `Suborigin` header whose value is the context's suborigin name.
Finally, the `Origin` header [[!RFC6454]] value must use the serialized suborigin
value instead of the serializied origin, as described in [[#serializing]].
Similar changes are needed for responses from the server with the addition of an
`Access-Control-Allow-Suborigin` response header. Its value must match the
context's suborigin namespace value, or `*` to allow all suborigin namespaces.
At the same time, the `Access-Control-Allow-Origin` response header value must
be modified to use the serialized suborigin value instead of the serializied
origin, as described in [[#serializing]].
Issue: TODO(jww): Formal definition of the headers and responses w/grammars.
Also need to be explicit about `*` having same limitations as
`Access-Control-Allow-Origin` w/credentials.
## `postMessage` ## {#postmessage-ac}
Cross-origin messaging via <a>`postMessage`</a> requires that the recipient be
able to see the suborigin namespace of the message sender so it can make an
appropriate access control decision. When a message is sent from a suborigin
namespace, the receiver has the `event.origin` value set to the serialized
suborigin value instead of the serializied origin, as described in
[[#serializing]]. Additionally, a new `suborigin` property must be added to
the `MessageEvent` given to the receiver which contains the suborigin
namespace value.
Issue: TODO(jww): Formalize by updating the <a>postMessage</a> algorithm
## Workers ## {#workers-ac}
User agents MUST refuse to create or execute any workers in a page executing in
a sub-origin.
Issue: TODO: Formalize. Not sure what this will look like yet. Chrome and
Opera don't allow creation of workers from sandbox'd iframes/the 'null'
origin, but not sure if this formalized in a spec. If it is, should probably
just update whatever disallows creation from sandbox.
Note: This may change in the future, and suborigins may eventually be allowed to
register service workers, but, for now, allowing the creation of any workers,
including service workers and shared workers, from suborigins adds too many
complications. Applications can still create workers by creating an iframe of
a page not in a suborigin.
# Impact on Web Platform # {#impact}
Content inside a suborigin namespace is restricted in the same way that other
origins are restricted. There are some additional restrictions as well, in order
to simplfy some complicated cases, and there are also some loosening of
same-origin restrctions in order to facilitate and ease adoption of suborigins
for developers.
## Relationship with Sensitive Permissions ## {#sensitive-permissions}
User agents MUST prevent a page running in a suborigin from accessing stateful
mechanisms (e.g., localStorage, sessionStorage, document.cookie) tied to the
parent, physical origin. Instead, the user agent SHOULD create a new object
tied to the namespaced suborigin.
User agents MUST ignore modifications to the document.domain property of the
page.
Issue: TODO: This section should probably be broken up. At the very least,
needs to be formalized. Maybe just stick with creating a new execution
context, and then carving out the weird exceptions?
### Modifications to WebSockets ### {#websockets}
The <a
href="https://www.w3.org/TR/websockets/#the-websocket-interface">`WebSocket()`
constructor algrithm</a> [[!WebSockets]] is modified as follows:
After the current step 1, perform the following step:
2. If the <a href="https://www.w3.org/TR/html51/webappapis.html#environment-settings-object">origin property</a>
of the <em>client</em>'s
<a href="https://www.w3.org/TR/html51/webappapis.html#relevant-settings-object">relevant settings object</a>
has a <a>suborigin</a>, throw a
<a href="https://www.w3.org/TR/WebIDL-1/#h-idl-domexception-error-names">SecurityError exception</a>.
Note: Suborigins are likely to allow WebSockets in the future, but are
disabled until it can be decided how they should be protected.
# Framework # {#framework}
Note: These sections are tricky because, unlike physical origins, we can't
define suborigins in terms of URIs. Since the suborigin namespace is defined in
a header, not in the URI, we need to define them in terms of resources.
## Suborigin of a Resource ## {#suborigin-of-resource}
The suborigin of a resource is the value computed by the following algorithm:
<ol>
<li>
Let origin be the triple result from starting with step 1 of Section 4 of
the <a href="https://tools.ietf.org/html/rfc6454#section-4">Section 4</a> of
of the Origin specification. [[!RFC6454]]
</li>
<li>
If the HTTP response of the resource contains a valid
[suborigin] header, then let `suborigin-namespace` be the
value of the header.
</li>
<li>
Otherwise, let `suborigin-namespace` be `null`.
</li>
<li>
Return the pair `(origin, suborigin-namespace)`.
</li>
</ol>
## Comparing Physical Origins ## {#comparing-physical-origins}
Two suborigins are "the same physical origin" if, and only if, the physical
origin portion of the suborigin are identical, per [[RFC6454]]. In
particular:
* If the origin portions of the suborigin pairs are scheme/host/port
triples, the two suborigins are the same physical origin if, and only if,
they have identical schemes, hosts, and ports.
* An origin that is a globally unique identifier cannot be the same as an
origin that is a scheme/host/port triple.
Two resources are the <dfn>same-physical-origin</dfn> if their suborigins are
the same physical origin.
## Comparing Suborigins ## {#comparing-suborigins}
Two suborigins are "the same" if, and only if, they are identical. In
particular:
* By [[#comparing-physical-origins]], the suborigins must be
same-physical-origin.
* In addition, the `suborigin-namespace` portion of the suborigin pairs must
be identical. If both `suborigin-namespace` portions of the suborigin
pairs are null, this is considered identical.
Two resources are the same-origin if their suborigins are the same.
## Serializing Suborigins ## {#serializing}
This section defines how to serialize an origin to a unicode [[!Unicode6]]
string and to an ASCII [[!RFC0020]] string.
### Update Scheme/Host/Port Triple with Suborigin for Serialization ### {#update-triple}
Given a suborigin pair |pair|, the scheme/host/port triple of |pair| is
updated for serialization by the following algorithm:
1. Let |prefix| be the suborigin namespace portion of |pair|.
2. Append the string "." to |prefix|.
3. Prepend |prefix| to the host part of the origin triple in |pair|.
4. Append the string "-so" to the scheme part of the origin triple in |pair|.
### Unicode Serialization of a Suborigin ### {#unicode-serialization}
The Unicode serialization of a suborigin is the value returned by the following
algorithm:
1. If the origin portion of the suborigin pair is not a scheme/host/port
triple, then return the string
<pre>
null
</pre>
(i.e., the code point sequence U+006E, U+0075, U+006C, U+006C) and abort
these steps.
2. Otherwise, if the suborigin namespace portion of the suborigin pair is not
null, [[#update-triple]].
3. Proceed with step 1 of <a
href="https://tools.ietf.org/html/rfc6454#section-6.1">Section 6.1 in the
Origin specification</a> [[!RFC6454]].
### ASCII Serialization of a Suborigin ### {#ascii-serialization}
The ASCII serialization of a suborigin is the value returned by the following
algorithm:
1. If the origin portion of the suborigin pair is not a scheme/host/port
triple, then return the string
<pre>
null
</pre>
(i.e., the code point sequence U+006E, U+0075, U+006C, U+006C) and abort
these steps.
2. Otherwise, if the suborigin namespace portion of the suborigin pair is not
null, [[#update-triple]].
3. Proceed with step 1 of <a
href="https://tools.ietf.org/html/rfc6454#section-6.2">Section 6.2 in the
Origin specification</a> [[!RFC6454]].
## Interactions with the DOM ## {#dom-interactions}
### Cookies ### {#cookies}
Append the following to the list of conditions of {{Document}} objects that are
<a>cookie-averse</a> in Section 3.1.2 of HTML5's <a>resource metadata
management</a>:
* A {{Document}} who has a non-empty <a>suborigin namespace</a>, unless the <a
link-type="grammar">suborigin-policy-option</a> for the {{Document}} contains
the '<a>`unsafe-cookies`</a>' value.
Modify the paragraph following this list to read "scheme/host/port/suborigin
tuple" instead of "scheme/host/port tuple".
Note: A <a>cookie-averse</a> {{Document}} object has the property that direct
access to `document.cookie` returns the empty string, and assigning to
`document.cookie` has no effect whatsoever. However, that network cookies are
not affected and documents with different <a>suborigin namespaces</a> on the
same <a>physical origin</a> share the same cookies on the network.
Note: For practical purposes, this means that a developer cannot use
`document.cookie` directly because assignment and reading of the object are both
no-ops. However, a <a lt="cookie-averse">cookie-averse</a> {{Document}} may
still use getters and setters on the `cookie` property of the `document` object
and, in that way, may still simulate cookie access.
## Security Model Opt-Outs ## {#security-model-opt-outs}
For backwards compatibility, Suborigins provide several opt-opts from the
standard security model. A developer can choose to use these opt-outs by
specifying a <a>suborigin policy</a> in <a href="#the-suborigin-header">the
suborigin header</a>
Since these opt-outs weaken the security model of suborigins, developers SHOULD
NOT use these options unless they are required to make their application work.
The values of <a link-type="grammar">suborigin-policy-option</a> that may be
present in a <a>suborigin policy</a> have the following effects:
* '<dfn>`unsafe-postmessage-receive`</dfn>' When a message is sent <i>to</i> a
frame with a `postMessage` `target` of a serialized physical origin, but not a
serialized suborigin, if the frame has an execution context with a suborigin
where the scheme, host, and port match the `target`, but it also has a
suborigin namespace, if <a>`unsafe-postmessage-receive`</a> is set, it will
still receive the message.
<div class="example" id="unsafe-postmessage-send-ex">
`https://example.com` runs a map API at `https://example.com/maps` which is
embedded by many other websites. It provides a `postMessage()` API to place
markers on the map at locations the embedder chooses.
The developer would like to run `https://example.com/maps` in a suborigin
namespace "maps". However, when embedders send messages to the embedded
frame, because they are legacy uses from before the use of suborigins, they
send essages with a `target` of `https://example.com`, <em>not</em>
`https-so://maps.example.com`. Since the developer would still like this
frame to be able to provide the API to these legacy embedders, it can serve
the frame with the <a>`unsafe-postmessage-receive`</a> directive, which will
allow the frame to receive messages on behalf of `https://example.com`, even
though it is at `https-so://maps.example.com`.
</div>
* '<dfn>`unsafe-postmessage-send`</dfn>' When a message is sent <i>from</i> a
suborigin namespace with <a>`unsafe-postmessage-send`</a> set, the
`event.origin` value of the receiver should be set to the serialized
physical origin, not the serialized suborigin value. However, the
`event.suborigin` field should still be set to the name of the suborigin
namespace.
<div class="example">
Continuing the case in the above example, `https://example.com/maps` is a
mapping application that is commonly embedded in other sites. It provides a
`postMessage()` based API to place locations of the embedder's choosing on the
map. `httsp://example.com` would like to run the application in a suborigin
named "maps".
In response to queries to the API, `https://example.com/maps` may send
messages back to the embedder if, for example, a user clicks on one of the
locations. However, since the embedder may be legacy and not be aware of
suborigins, when it checks the `event.origin` protery of the
`MesseageEvent`, if it sees `https-so://maps.example.com` as the origin, it
will reject the message as a potential attack. Thus, `https://example.com`
may use the <a>`unsafe-postmessage-send`</a> directive to allow its messages
to appear with the origin of the physical origin, in this case
`https://example.com`.
</div>
* '<dfn>`unsafe-cookies`</dfn>' When an execution context with a suborigin
namespace has <a>`unsafe-cookies`</a> set, the
execution context should not have a fresh cookie jar for the suborigin
namespace, and instead, the cookie jar should be shared with the null
suborigin for the execution context.
Issue: TODO(devd): Write an example that makes clear that this is extremely
dangerous and should not be used if you have Real Deal auth and csrf cookies
used.
* '<dfn>`unsafe-credentials`</dfn>' All cross-origin requests to the <a>physical origin</a> for the execution
context will include <a>credentials</a>.
Specifically, update the step 2 of <a>HTTP-network-or-cache fetch</a> in
the Fetch spec [[!FETCH]] to read:
2. Let credentials flag be set if one of
* `request`'s credentials mode is "include"
* `request`'s credentials mode is "same-origin" and request's response
tainting is "basic"
* `request`'s credentials mode is "same-origin" and `request`'s
<a>environment settings object</a> has the `suborigin unsafe
credentials` flag set and the request's <a>current url</a> is
<a>same-physical-origin</a> with <a>request origin</a>.
Note: This has several important, practical effects. If the <a>credentials
mode</a> of a `fetch()` is set to "same-origin", and if it is to the same
physical origin as the current execution context, credentials will be
included. This means that if the `crossorigin` attribute on elements such
as `<img>` are set to `anonymous`, credentials will be sent if the origin
of the URL is the same physical origin. Similarly, if making an
<a>XMLHttpRequest</a>, setting this flag is equivalent to setting
<a>`withCredentials`</a> to `true` for requests to the same physical
origin. This means that it credentials will be sent even for requests to
another suborigin at the same physical origin.
Issue: TODO(jww,aaj): Provide an example of why this might be needed.
Issue: TODO: These opt-out descriptions should probably be moved to the
individual sections where the behaviors are discussed (i.e. postMessage and
cookies). These just need to be fleshed out anyway, and examples and reasons
need to be given. Also, they need to update the processing model, such as
adding appropriate flags to the environment settings object.
# Practical Considerations in Using Suborigins # {#practical-considerations}
Using suborigins with a Web application should be relatively simple. At the most
basic level, if you have an application hosted on `https://example.com/app/`,
and all of its resources are hosted at subpaths of `/app`, it requires that the
server set a Content Security Policy on all HTTP requests to subpaths of `/app`
that contain the header `suborigin: namespace`, where `namespace` is of the
application's choosing. This will ensure that the user agent loads all of these
resources into the suborigin `namespace` and will enforce this boundary
accordingly.
Additionally, if your application allows cross-origin requests, instead of
adding the usual `Access-Control-Allow-Origin` header for cross-origin requests,
the server must add the `Access-Control-Allow-Finer-Origin` and
`Access-Control-Allow-Suborigin` headers, as defined in [[#cors-ac]].
In the client-side portion of the application, if `postMessage` is used, the
application must be modified so it does not check the `event.origin` field.
Instead, it should check `event.finerorigin` and additionally the
`event.suborigin` fields, as they are defined in [[#postmessage-ac]].
Issue: TODO(devd): Flesh out the above and make sure it covers all it needs to
cover.
# Security Considerations # {#security-considerations}
## Presentation of Suborigins to Users ## {#presentation-to-users}
A complication of suborigins is that while they provide a meaningful security
for an application, that boundary makes much less sense to a user. That is,
physical origins provide a security boundary at a physical level: separate
scheme, hosts, and ports map to real boundaries external of a given application.
However, suborigins as a boundary only makes sense <em>within the context of the
program logic itself</em>, and there is no meaningful way for users to make
decisions based on suborigins a priori.
Therefore, suborigins should be used only internally in a user agent and MUST
NOT be presented to users at all. For example, user agents must never present
suborigins in link text or a URL bar.
## Not Overthrowing Same-Origin Policy ## {#not-overthrowing-sop}
Suborigins do not fundamentally change how the same-origin policy works. An
application without suborigins should work identically to how it always has, and
even in an application with suborigins, the same-origin policy still applies as
always. In fact, this document defines suborigins within the context of the
same-origin policy so that, in theory, serialized suborigins can be thought of
as a just a special case of the traditional same-origin policy.
## Problems with Serialized Representation ## {#serialization-concerns}
Issue: TODO: Need to list concerns with serialization, e.g. apps that don't
recognize the suborigin serialization, esp. if they blocklist origins.