├── .pr-preview.json ├── .travis.yml ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE.md ├── Makefile ├── README.md ├── index.html ├── index.src.html ├── published ├── 2015-10-FPWD.html └── default.css └── w3c.json /.pr-preview.json: -------------------------------------------------------------------------------- 1 | { 2 | "src_file": "index.src.html", 3 | "type": "bikeshed", 4 | "params": { 5 | "force": 1 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | python: 3 | - "3.7" 4 | before_install: 5 | - sudo apt-get update -qq 6 | - sudo apt-get install -qq python-dev python-pip libxslt1-dev libxml2-dev 7 | - sudo pip install pygments 8 | install: 9 | - git clone --depth=1 --branch=master https://github.com/tabatkins/bikeshed.git ./bikeshed 10 | - sudo pip install --editable ./bikeshed 11 | - bikeshed update 12 | script: 13 | - bikeshed spec ./index.src.html 14 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct 2 | 3 | All documentation, code and communication under this repository are covered by the [W3C Code of Ethics and Professional Conduct](https://www.w3.org/Consortium/cepc/). 4 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Web Application Security Working Group 2 | 3 | Contributions to this repository are intended to become part of Recommendation-track documents 4 | governed by the [W3C Patent Policy](https://www.w3.org/Consortium/Patent-Policy/) and 5 | [Document License](https://www.w3.org/Consortium/Legal/copyright-documents). To contribute, you must 6 | either participate in the relevant W3C Working Group or make a non-member patent licensing 7 | commitment. 8 | 9 | If you are not the sole contributor to a contribution (pull request), please identify all 10 | contributors in the pull request's body or in subsequent comments. 11 | 12 | To add a contributor (other than yourself, that's automatic), mark them one per line as follows: 13 | 14 | ``` 15 | +@github_username 16 | ``` 17 | 18 | If you added a contributor by mistake, you can remove them in a comment with: 19 | 20 | ``` 21 | -@github_username 22 | ``` 23 | 24 | If you are making a pull request on behalf of someone else but you had no part in designing the 25 | feature, you can remove yourself with the above syntax. 26 | 27 | # Tests 28 | 29 | See [CONTRIBUTING.md](https://github.com/w3c/webappsec/blob/master/CONTRIBUTING.md). 30 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | All documents in this Repository are licensed by contributors under the [W3C Document 2 | License](http://www.w3.org/Consortium/Legal/copyright-documents). 3 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | all: index.html 2 | 3 | force: 4 | bikeshed -f spec -l ./index.src.html 5 | 6 | index.html: index.src.html 7 | bikeshed -f spec -l ./index.src.html 8 | 9 | publish: 10 | git push origin master master:gh-pages 11 | 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Confinement with Origin Web Labels 2 | ======================= 3 | 4 | [](https://gitter.im/w3c/webappsec-cowl?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 5 | [](https://travis-ci.org/w3c/webappsec-cowl) 6 | 7 | * COWL is being specified in `index.src.html` 8 | * [Published Editor's Draft](https://w3c.github.io/webappsec-cowl/) 9 | * [W3C First Public Working Draft](http://www.w3.org/TR/2015/WD-COWL-20151015/) 10 | 11 | -------------------------------------------------------------------------------- /index.src.html: -------------------------------------------------------------------------------- 1 |
3 | Status: ED 4 | Group: WebAppSec 5 | TR: https://www.w3.org/TR/COWL/ 6 | ED: https://w3c.github.io/webappsec-cowl/ 7 | Shortname: COWL 8 | Level: 1 9 | Editor: Deian Stefan, Intrinsic and UC San Diego, deian@intrinsic.com 10 | Editor: Abdulrahman Alkhelaifi, UC San Diego, aalkhela@eng.ucsd.edu 11 | Abstract: 12 | This specification defines an API for specifying privacy and 13 | integrity policies on data, in the form of origin labels, and a 14 | mechanism for confining iframes according to such policies. This 15 | allows Web application authors and server operators to shared data 16 | with untrusted—buggy but not malicious—code (e.g., in a mashup 17 | scenario) yet impose restrictions on how the code running in an iframe can 18 | share the data further. 19 | Indent: 2 20 | Version History: https://github.com/w3c/webappsec-cowl/commits/master/index.src.html 21 | Boilerplate: omit conformance, omit feedback-header 22 | !Participate: File an issue (open issues) 23 | Inline Github Issues: true 24 | Markup Shorthands: css off, markdown on 25 |26 |
27 | spec:url 28 | type: dfn 29 | text: url; for: / 30 |31 |
32 | spec: CORS; urlPrefix: https://www.w3.org/TR/cors/ 33 | type: dfn 34 | text: CORS 35 | text: Access-Control-Allow-Origin; url: access-control-allow-origin-response-header 36 | spec: IndexedDB; urlPrefix: https://www.w3.org/TR/IndexedDB-2/ 37 | type: dfn 38 | text: IndexedDB 39 | spec: WEBMESSAGING; urlPrefix: https://www.w3.org/TR/webmessaging/ 40 | type: method 41 | text: postMessage(); url: dom-window-postmessage 42 | type: dfn 43 | text: posting messages; url: posting-messages 44 | text: owner; url: concept-port-owner 45 | text: Message Port; url: messageport 46 | text: MessagePort postMessage(); url: dom-messageport-postmessage 47 | text: cross-document messaging; url: web-messaging 48 | spec: WORKERS; urlPrefix: https://www.w3.org/TR/workers/ 49 | type: dfn 50 | text: Worker 51 | text: Web Worker; url: infrastructure 52 | spec: CSP2; urlPrefix: https://www.w3.org/TR/CSP2/ 53 | type: dfn 54 | text: content-security-policy 55 | type: dfn 56 | text: host-source; url: host_source 57 | spec: HTML5; urlPrefix: https://www.w3.org/TR/html5/ 58 | type: element-attr 59 | urlPrefix: embedded-content-0.html 60 | text: sandbox; for: iframe; url: attr-iframe-sandbox 61 | text: contentDocument; for: iframe; url: dom-iframe-contentdocument 62 | type: dfn 63 | urlPrefix: webappapis.html 64 | text: environment settings object; url: settings-object 65 | text: incumbent settings object; url: incumbent-settings-object 66 | urlPrefix: embedded-content-0.html 67 | text: iframe; url: the-iframe-element 68 | urlPrefix: browsers.html 69 | text: browsing context; url: windows 70 | text: top-level browsing context; url: top-level-browsing-context 71 | text: active sandboxing flag set; url: active-sandboxing-flag-set 72 | text: sandboxing flag set; url: sandboxing-flag-set 73 | text: forced sandboxing flag set; url: forced-sandboxing-flag-set 74 | text: sandboxed plugins browsing context flag;url: sandboxed-plugins-browsing-context-flag 75 | text: sandboxed document.domain browsing context flag;url: sandboxed-document.domain-browsing-context-flag 76 | text: sandboxed origin browsing context flag;url: sandboxed-origin-browsing-context-flag 77 | text: sandboxed navigation browsing context flag;url: sandboxed-navigation-browsing-context-flag 78 | text: sandboxed auxiliary navigation browsing context flag;url: sandboxed-auxiliary-navigation-browsing-context-flag 79 | text: sandboxed top-level navigation browsing context flag;url: sandboxed-top-level-navigation-browsing-context-flag 80 | text: navigating; url: navigate 81 | urlPrefix: infrastructure.html 82 | text: structured clone; url: structured-clone 83 | text: structurally cloned; url: structured-clone 84 | text: structurally clonable; url: structured-clone 85 | text: internal structured cloning algorithm; url: internal-structured-cloning-algorithm 86 | text: strictly split a string 87 | text: skip whitespace 88 | text: collect a sequence of characters 89 | text: strip leading and trailing whitespace 90 | text: strip and collapse whitespace 91 | text: ASCII case-insensitive 92 | text: space characters; url: space-character 93 | urlPrefix: dom.html 94 | text: script from reading from or writing to the document.cookie IDL attribute; url: sandboxCookies 95 | type: interface 96 | urlPrefix: browsers.html 97 | text: Window; url: window 98 | urlPrefix: dom.html 99 | text: Document; url: the-document-object 100 | spec: feature-policy; urlPrefix: https://wicg.github.io/feature-policy/ 101 | type: dfn 102 | text: container policy; url: container-policies 103 | spec: RFC6454; urlPrefix: https://tools.ietf.org/html/rfc6454 104 | type: dfn 105 | text: origin; url: section-3.2 106 | text: globally unique identifier; url: section-2.3 107 | spec: RFC4122; urlPrefix: https://tools.ietf.org/html/rfc4122 108 | type: dfn 109 | text: UUID; url: section-3 110 | spec: URL; urlPrefix: https://www.w3.org/TR/url/ 111 | type: interface 112 | text: URL; url: concept-url 113 | type: dfn 114 | text: scheme; url: concept-url-scheme 115 | type: attribute 116 | text: host; for: URL; url: concept-url-host 117 | text: path; for: URL; url: concept-url-path 118 | text: port; for: URL; url: concept-url-port 119 | text: scheme; for: URL; url: concept-url-scheme 120 | spec: RFC7159; urlPrefix: https://tools.ietf.org/html/rfc4627 121 | type: dfn 122 | text: JSON; url: section-2 123 | text: JSON object; url: section-2.2 124 | text: JSON array; url: section-2.3 125 | text: JSON stringification; url: section-2 126 | spec: html-comms; urlPrefix: https://html.spec.whatwg.org/multipage/comms.html 127 | type: dfn 128 | text: Server-sent events; url: server-sent-events 129 | spec: whatwg-messaging; urlPrefix: https://html.spec.whatwg.org/multipage/web-messaging.html 130 | type: dfn 131 | text: channel messaging; url: channel-messaging 132 | text: broadcast channels; url: broadcasting-to-other-browsing-contexts 133 | spec: WebRTC; urlPrefix: https://www.w3.org/TR/webrtc/ 134 | type: dfn 135 | text: WebRTC 136 | spec: XHR; urlPrefix: https://xhr.spec.whatwg.org/ 137 | type: dfn 138 | text: XMLHttpRequest 139 | text: final MIME type; url: final-mime-type 140 | text: author request headers; url: author-request-headers 141 | type: attribute 142 | text: responseType; url: dom-xmlhttprequest-responsetype 143 | text: response; url: dom-xmlhttprequest-responsetype 144 | type: interface 145 | text: XMLHttpRequest; url: xmlhttprequest 146 | type: enum 147 | text: XMLHttpRequestResponseType; url: xmlhttprequestresponsetype 148 | type: method 149 | text: send(); url: the-send()-method 150 | text: open(); url: the-open()-method 151 | spec: FETCH; urlPrefix: https://fetch.spec.whatwg.org/ 152 | type: dfn 153 | text: fetching 154 | text: main fetch; url: main-fetch 155 | text: request; url: concept-request 156 | text: response; url: concept-response 157 | text: body; url: concept-response-body 158 | text: client; url: concept-request-client 159 | text: type; url: concept-request-type 160 | text: destination; url: concept-request-destination 161 | text: header; url: concept-header 162 | text: name; url: concept-header-name 163 | text: value; url: concept-header-value 164 | text: header list; url: concept-header-list 165 | text: network error; url: concept-network-error 166 | text: extracting a MIME type; url: concept-header-extract-mime-type 167 | text: navigation request; url: navigation-request 168 | spec: ENCODING; urlPrefix: https://encoding.spec.whatwg.org/ 169 | type: dfn 170 | text: utf-8 decode; url: utf-8-decode 171 | spec: WEBSTORAGE; urlPrefix: https://www.w3.org/TR/webstorage/ 172 | type: attribute 173 | text: localStorage; url: the-localstorage-attribute 174 | spec: RFC5234; urlPrefix: https://tools.ietf.org/html/rfc5234 175 | type: dfn 176 | text: ALPHA; url: appendix-B.1 177 | text: DIGIT; url: appendix-B.1 178 | text: WSP; url: appendix-B.1 179 | text: VCHAR; url: appendix-B.1 180 | spec: DOM-Parsing; urlPrefix: https://w3c.github.io/DOM-Parsing/ 181 | type: dfn 182 | text: serialized; url: dfn-concept-fragment-serializing-algorithm 183 | spec: WEBIDL2; urlPrefix: https://heycam.github.io/webidl/ 184 | type: interface 185 | text: object; url: idl-object 186 | text: DOMString; url: idl-DOMString 187 | type: dfn 188 | text: exposed; url: dfn-exposed 189 | text: SecureContext; url: SecureContext 190 | spec: REFERRER-POLICY; urlPrefix: https://w3c.github.io/webappsec-referrer-policy/ 191 | type: dfn 192 | text: determine request's referrer algorithm; url: determine-requests-referrer 193 | text: referrer policy; url: referrer-policy 194 | spec: WEBSOCKETS; urlPrefix: https://www.w3.org/TR/websockets/ 195 | type: method 196 | text: WebSocket(); url: dom-websocket 197 | type: dfn 198 | text: make disappear; url: make-disappear 199 |200 | 201 | 202 |
203 | { 204 | "DCLabels": { 205 | "authors": ["Deian Stefan", "Alejandro Russo", "David Mazieres", "John C. Mitchell"], 206 | "title": "Disjunction Category Labels", 207 | "href": "http://www.scs.stanford.edu/~deian/pubs/stefan:2011:dclabels.pdf", 208 | "publisher": "Springer Berlin Heidelberg" 209 | }, 210 | "COWL-OSDI": { 211 | "authors": ["Deian Stefan", "Edward Z. Yang", "Petr Marchencko", "Alejandro Russo", "David Herman", "Brad Karp", "David Mazieres", "John C. Mitchell"], 212 | "title": "Protecting Users by Confining JavaScript with COWL", 213 | "href":"https://www.usenix.org/system/files/conference/osdi14/osdi14-paper-stefan.pdf", 214 | "publisher": "USENIX" 215 | }, 216 | "URL": { 217 | "authors": [ "Anne van Kesteren", "Sam Ruby" ], 218 | "title": "URL", 219 | "href": "https://www.w3.org/TR/url", 220 | "status": "WD", 221 | "publisher": "W3C" 222 | }, 223 | "WEBIDL2": { 224 | "authors": [ "Cameron McCormack", "Boris Zbarsky" ], 225 | "title": "Web IDL (Second Edition)", 226 | "href": "https://heycam.github.io/webidl/", 227 | "status": "ED", 228 | "publisher": "W3C" 229 | } 230 | } 231 |232 | 233 | 242 |
https://example.com
can specify that a
268 | password is confidential to https://example.com
(and
269 | thus should only be disclosed to https://example.com
)
270 | before sharing it with a third-party password strength checking
271 | service. In turn, COWL ensures that the third-party service, which
272 | necessarily computes on the sensitive password, is confined and
273 | respects the policy on the password: COWL disallows it from
274 | disclosing the password to any origin other than
275 | https://example.com
.
276 |
277 | COWL enforces such policies by confining code at the
278 | browsing context-level, according to the sensitivity (i.e., the label)
279 | of the data the code has observed. To reap the greatest benefits of
280 | COWL, authors will need to compartmentalize applications into
281 | multiple contexts (e.g., iframes).
282 |
283 | In the existing model, any page served from an origin has the
284 | ambient, implicit authority of that origin. This documents
285 | generalizes this notion of authority and gives authors explicit
286 | control over it with privileges. For example, by default, a
287 | page whose origin is https://example.com
has the
288 | privilege for https://example.com
. This gives the page
289 | the authority to arbitrarily disseminate data sensitive to
290 | https://example.com
; to be backwards-compatible,
291 | COWL does not confine the page when reading data sensitive to
292 | https://example.com
. However, COWL allows the author
293 | to run iframes with "weaker" delegated privileges (e.g., one
294 | corresponding the current user at https://example.com
)
295 | or to drop the privilege altogether.
296 |
297 | COWL is intended to be used as a defense-in-depth mechanism that can restrict
298 | how untrusted—buggy but not malicious—code handles sensitive
299 | data. Given the complexities of browser implementations and the presence of
300 | covert channels, malicious code may be able to exfiltrate data. Authors
301 | should still use discretionary access control mechanisms, such as CSP and
302 | CORS, to restrict access to the data in the first place.
303 |
304 | Access-Control-Allow-Origin
header and the
312 | targetOrigin
argument to
313 | postMessage()
) provide a way for restricting
314 | which origins may access the shared data. But, once content
315 | has access to data it can usually disseminate it without
316 | restrictions. While CSP can be used to confine code, i.e., restrict
317 | how confidential data is disseminated, setting a correct CSP policy
318 | (as to confine code) is difficult and limited to content the author
319 | has control over. Indeed, sharing confidential data in the existing
320 | model almost always requires the sender to trust the receiver not to
321 | leak the data, accidentally or otherwise. COWL provides a
322 | defense-in-depth option for protecting data confidentiality and
323 | integrity. In particular, with COWL:
324 |
325 | 1. Authors should be able to specify confidentiality and integrity
326 | policies on data in terms of origin labels: the origins to whom the
327 | data is confidential and the origins that endorse the data. This
328 | allows authors to share sensitive data with third-party content and
329 | impose restrictions on the origins with which it can communicate
330 | once it inspects the sensitive data. Dually, it allows authors to
331 | share data via intermediate content while retaining its integrity.
332 |
333 | 2. Authors should be able to run code with least privilege
334 | by restricting the origins the code can communicate with and thus
335 | how it can disseminate sensitive data.
336 |
337 | 3. Authors should be able to privilege separate
338 | applications by compartmentalizing them into separate contexts that
339 | have delegated privileges.
340 |
341 | https://example.com
359 | wishes to use a third-party password strength checker provided by
360 | https://untrusted.com
. To protect the
361 | confidentiality of the password, the
362 | https://example.com
application can use COWL to
363 | run the checker code in a cowl iframe and associate a
364 | confidentiality policy, in the form of a label, with the password
365 | before sending it to the untrusted service:
366 |
367 |
368 | // Create new policy using Labels that specifies that the password is sensitive
369 | // to https://example.com and should only be disclosed to this origin:
370 | var policy = new Label(window.location.origin);
371 |
372 | // Associate the label with the password:
373 | var labeledPassword = new LabeledObject(password, { confidentiality: policy });
374 |
375 | // Send the labeled password to the checker iframe:
376 | checker.postMessage(labeledPassword, "https://untrusted.com");
377 |
378 | // Register listener to receive a response from checker, etc.
379 |
380 |
381 | Once the checker inspects the label-protected object, i.e., the
382 | password, COWL limits the iframe to communicating with origins that
383 | preserve the password's confidentiality (in this case,
384 | https://example.com
). This policy is enforced mandatorily,
385 | even if the https://untrusted.com
iframe sends the password to
386 | yet another iframe.
387 |
388 | Note, until the checker actually inspects the labeled password, it
389 | can freely communicate with any origins, e.g., with
390 | https://untrusted.com
. This is important since the
391 | checker may need to fetch resources (e.g., regular expressions) to
392 | check the password strength. It's also safe—the checker has
393 | not inspected the sensitive password, and thus need not be
394 | confined.
395 | Sec-COWL
(described in
411 | [[#response-header]]) whose value contains the sensitivity of the data in the
412 | form of a serialized confidentiality label. In turn, COWL enforces
413 | the label restrictions on the third-party code.
414 |
415 | https://provider.com
uses a
417 | CORS response header to grant https://mashup.com
418 | access to a resource. The operator also sets a COWL header to
419 | specify that the resource is confidential to
420 | https://provider.com
and should not be disseminated
421 | arbitrarily:
422 |
423 |
424 | Access-Control-Allow-Origin: https://mashup.com
425 | Sec-COWL: data-confidentiality https://provider.com
426 |
427 |
428 | COWL only allows a https://mashup.com
context to read
429 | the sensitive response if the label restrictions of the response
430 | are respected, i.e., if the code can only communicate with
431 | https://provider.com
.
432 |
433 | Note, COWL only allows the code to inspect the response if the context
434 | labels, which dictate the context's ability to communicate, are more
435 | restricting than the labels of the response. A more permissive approach,
436 | which does not require the context give up its ability to communicate
437 | arbitrarily is to use a labeled JSON response. The mashup XHR example shows how authors can
439 | accomplish this.
440 | https://university.edu
wished to isolate
456 | different parts of their site according to users. The server
457 | operator can weaken the privilege of a page when serving user
458 | content by providing a response header field named
459 | Sec-COWL
460 | (see [[#response-header]]) whose value contains
461 | a serialized delegated privilege. For example, for any
462 | content under https://university.edu/~user1
, the
463 | following header is set:
464 |
465 |
466 | Sec-COWL: ctx-privilege 'self' OR app:user1
467 |
468 |
469 | Having this privilege can be understood as having the authority of
470 | user1
's part of the application's origin. COWL ensures that
471 | the content of this page cannot interfere with the content of
472 | https://university.edu
or that of another user e.g.,
473 | user2
. For example, the content cannot modify
474 | https://university.edu
cookies or the DOM of another
475 | https://university.edu
page.
476 |
477 | This delegated privilege also ensures that the
478 | content cannot disseminate data sensitive to another user (e.g.,
479 | user2
) arbitrarily—without being confined,
480 | it can only disseminate user1
's data on
481 | https://university.edu
.
482 | Of course, this requires the server operator to label
483 | sensitive data (e.g., when sending it to the user agent) appropriately
484 | (e.g., user2
's data is labeled
485 | Label("https://university.edu").or("app:user2")
).
486 |
487 | The sub-origin isolation in
488 | JavaScript example shows how this can be implemented using the
489 | COWL JavaScript APIs.
490 | https://example.com
can drop privileges
509 | in JavaScript:
510 |
511 |
512 | // Drop privileges, by setting the context privilege to an empty privilege:
513 | COWL.privilege = new Privilege();
514 |
515 | // Load untrusted library
516 |
517 |
518 | Or, by setting the content's initial privilege to the empty
519 | privilege using
520 | Sec-COWL
response header:
521 |
522 |
523 | Sec-COWL: ctx-privilege 'none'
524 |
525 |
526 | or
part of labels can be used to easily
537 | categorize differently-sensitive data.) To this end, the author
538 | should run the context with a delegated privilege instead of
539 | the empty privilege. The above [[#examples-isolation]] shows
540 | one such example.
541 | sandbox
)
562 | and SHOULD be used as an additional layer of defense. The goal of this
563 | specification is not to replace existing discretionary access control
564 | mechanisms.
565 | Sec-COWL
HTTP request header to
571 | provide server operators with information about the context that made the
572 | request (e.g., the context privilege). This header is sent in
573 | accordance to thereferrer policy [[REFERRER-POLICY]]. However,
574 | since the request header may contain serialized application-specific
575 | principals, this can have privacy implications when the principals
576 | encode private user information. It is RECOMMENDED that authors and
577 | server operators not include any private information in
578 | labels—labels SHOULD be treated as public data.
579 |
580 | Label("https://a.com").or("https://b.com")
,
616 | when associated with a context,
617 | restricts the context to sending data to
618 | https://a.com
or https://b.com
, but
619 | no other origins.
620 | This context label reflects the fact the context may contain data
621 | that is sensitive to either https://a.com
or
622 | https://b.com
; it is thus only safe for it to
623 | communicate to these origins.
624 |
625 | Note, because the context can communicate data to either origin,
626 | another context associated with the more restricting
627 | label Label("https://a.com")
cannot
628 | send it data. Doing so would allow for data confidential
629 | to https://a.com
to be leaked to https://b.com
.
630 | Label("https://a.com").or("https://b.com")
,
635 | when associated with a context, restricts the context to
636 | receiving data from (a context or server) that is at least as
637 | trustworthy as https://a.com
or
638 | https://b.com
. This context label ensures that
639 | the code running in the context can only be influenced by data
640 | which either https://a.com
or
641 | https://b.com
endorse.
642 | https://example.com
page that
653 | receives a labeled object (e.g., via postMessage())
654 | with the following labels:
655 |
656 | * Confidentiality:
657 | Label("https://example.com")
.
658 | This label indicates that the object is sensitive to
659 | https://example.com
.
660 |
661 | * Integrity:
662 | Label("https://a.com")
. This
663 | label indicates that the object has been endorsed by
664 | https://a.com
. If
665 | https://example.com
received the message from an
666 | intermediary https://b.com
context, this label
667 | reflects the fact that the object (produced by
668 | https://a.com
) was not tampered.
669 | "unique:"
string as a
679 | prefix to a UUID (see unique-principal-expression).
680 |
681 | - An application-specific principal is used by authors to
682 | encode application-specific security entities (e.g., users, services);
683 | they are strings prefixed by the "app:"
string (see
684 | app-principal-expression). Such principals, when used in
685 | labels, typically only make sense if combined (e.g., as a
686 | disjunction) with origin principals or unique principals.
687 |
688 |
689 | 3. Mathematically, a label is a conjunctive normal form
690 | formula over principals [[DCLabels]].
691 |
692 | A label is in normal form if reducing it according to
693 | the label normal form reduction algorithm produces the
694 | same value.
695 |
696 | Two labels are equivalent if their normal form
697 | values are mathematically equal.
698 |
699 | A label A subsumes (or is more
700 | restricting than) another label
701 | B if the result of running the label subsumption
702 | algorithm on the normal forms of A and
703 | B returns true
. Labels are partially
704 | ordered according to this subsumes relation.
705 |
706 | The current confidentiality label is the
707 | confidentiality label associated with the current context.
708 | [[#framework-context-labels]] specifies how labels are associated with
709 | contexts.
710 |
711 | The current integrity label is the
712 | integrity label associated with the current context.
713 | [[#framework-context-labels]] specifies how labels are associated with
714 | contexts.
715 |
716 | When reading a labeled object, a context gets
717 | tainted, i.e., its context labels are updated by
718 | invoking context tainting algorithm, to reflect that it
719 | has read sensitive data, or data of potentially different
720 | trustworthiness, and should be confined accordingly.
721 |
722 | https://a.com
whose
734 | current confidentiality label is Label("https://a.com").and("https://b.com")
.
736 | This label confines the context to only communicating with
737 | entities whose labels are at least as restricting as this
738 | label. For example, it restricts the context from
739 | communicating with a context labeled Label("https://b.com")
, since doing
741 | so could leak https://a.com
data to
742 | https://b.com
. It similarly prevents the context
743 | from communicating with https://a.com
.
744 |
745 | But, suppose that the context's current privilege
746 | corresponds to Label("https://a.com")
747 | (afterall, the context originated from https://a.com
).
748 | Then, the context would be able to bypass some of the
749 | restrictions imposed by the context label. Specifically, the
750 | context would be able to communicate with
751 | https://b.com
; the privilege confers it the right
752 | to declassify https://a.com
data to
753 | https://b.com
. Indeed, when taking this privilege
754 | into consideration, the effective confidentiality label
755 | of the context is Label("https://b.com")
.
756 |
757 | Note, the privilege does not allow the context to bypass any
758 | label restrictions. For example, it does not allow the context
759 | to communicate with https://a.com
since doing so
760 | could leak https://b.com
data.
761 |
762 | https://a.com
context whose
780 | current integrity label is
781 | Label("https://a.com").or("https://b.com")
.
782 | This label confines the context to only communicating with
783 | entities that are at most as trustworthy as this
784 | label. For example, it restricts the context from
785 | communicating with a context whose current integrity
786 | label is
787 | Label("https://a.com")
, since doing
788 | so would potentially corrupt https://a.com
data
789 | (e.g., by allowing https://b.com
to influence the
790 | computation).
791 |
792 | But, if the context's current privilege corresponds
793 | to Label("https://a.com")
, the
794 | context would be able to bypass some of these integrity restrictions.
795 | Specifically, the context would be able to communicate with the
796 | more-trustworthy context (labeled
797 | Label("https://a.com")
) since the
798 | privilege confers it the right to endorse (or vouch for) its
799 | context on behalf of https://a.com
. Indeed, when
800 | taking privileges into account, the effective integrity
801 | label of the context is Label("https://a.com")
.
803 |
804 | Note, the privilege cannot be used to bypass any integrity
805 | restrictions. For example, it does not allow the context to
806 | communicate with a context whose integrity label is Label("https://b.com")
.
808 | Sec-COWL
HTTP header field.
861 | Sec-COWL
HTTP response header can be
868 | used to explicitly control the authority of a context by setting
869 | the context privilege.
870 | 898 | [Constructor, Constructor(DOMString principal), Exposed=(Window, Worker)] 899 | interface Label { 900 | boolean equals(Label other); 901 | boolean subsumes(Label other, optional Privilege priv); 902 | 903 | Label and((Label or DOMString) other); 904 | Label _or((Label or DOMString) other); 905 | 906 | stringifier; 907 | }; 908 |909 | 910 |
TypeError
exception [[!ECMA-262]] and
925 | terminate this algorithm.
926 |
927 | 2. Else, it MUST return a new Label that contains a
928 | label set of a single disjunction set, which
929 | itself MUST contain the principal corresponding to the
930 | principal argument.
931 | true
if the Label on
939 | which the method has been called is equivalent to the
940 | other
941 | parameter; otherwise it MUST return false
.
942 | lab.and(priv.asLabel())
.
954 |
955 | 3. Return true
if lab subsumes the
956 | other
957 | parameter and false
otherwise.
958 | DOMString
, run the following sub-steps:
968 |
969 | 1. Set O to the result of invoking the
970 | Label(other) constructor with other as an argument, if the
972 | constructor did not raise an exception.
973 |
974 | 2. Else, re-throw the exception and terminate this algorithm.
975 |
976 | 2. Return a new normal form Label
977 | that is equivalent to a label whose
978 | label set contains the disjunction sets of
979 | O and the Label on which
980 | the method was invoked.
981 | DOMString
, run the following sub-steps:
991 |
992 | 1. Set O to the result of invoking the
993 | Label(other) constructor with other as an argument, if the
995 | constructor did not raise an exception.
996 |
997 | 2. Else, re-throw the exception and terminate this algorithm.
998 |
999 | 2. Return a new Label, in normal form,
1000 | which is equivalent to adding each element of each
1001 | disjunction set of O's label set to
1002 | each disjunction set of the label set of the
1003 | Label on which the method was called.
1004 |
1053 | // C: Public data.
1054 | // I: Non-endorsed/untrustworthy data.
1055 | var empty = new Label();
1056 |
1057 | // C: Data confidential to a.com.
1058 | // I: Data endorsed/trusted by a.com.
1059 | var a = new Label("https://a.com");
1060 |
1061 | // C: Data confidential to b.com.
1062 | // I: Data endorsed/trusted by b.com.
1063 | var a = new Label("https://b.com");
1064 |
1065 | // C: Data confidential to both a.com and b.com
1066 | // I: Data endorsed/trusted by a.com and b.com.
1067 | var aANDb = new Label("https://a.com").and("https://b.com");
1068 |
1069 | // C: Data confidential to either a.com or b.com.
1070 | // I: Data endorsed/trusted by either a.com or b.com.
1071 | var aORb = new Label("https://a.com").or("https://b.com");
1072 |
1073 | Examples of label comparisons with intuition for the semantics.
1074 |
1075 | // C: Data confidential to a.com (b.com) data is more sensitive than public data.
1076 | // I: Data endorsed by a.com (b.com) is more trustworthy than non-endorsed/untrustworthy data.
1077 | a.subsumes(empty) === true;
1078 | b.subsumes(empty) === true;
1079 |
1080 | // C: Data that is confidential to a.com and b.com is more
1081 | // confidential than data that is only sensitive to a.com (b.com).
1082 | // I: Data that is endorsed/trusted by both a.com and b.com is
1083 | // more trustworthy than data endorsed only by a.com (b.com).
1084 | aANDb.subsumes(a) === true;
1085 | aANDb.subsumes(b) === true;
1086 |
1087 | // C: Data that that is confidential to a.com (b.com) is not comparable to
1088 | // data that is confidential to b.com (a.com).
1089 | // I: Data that that is endorsed by a.com (b.com) is not comparable to
1090 | // data that is endorsed by b.com (a.com).
1091 | a.subsumes(b) === false;
1092 | b.subsumes(a) === false;
1093 |
1094 | // C: Data that is confidential to a.com (b.com) is more confidential than data that is
1095 | // confidential to either a.com or b.com. Alternative intuition: data that can be read by
1096 | // a.com or b.com can be read by an entity that can read a.com (b.com) data alone.
1097 | // I: Data that is endorsed by a.com (b.com) is more trustworthy than data that is endorsed
1098 | // by either a.com or b.com. Alternative intuition: an entity that trusts data endorsed
1099 | // by either a.com or b.com necessarily trusts data endorsed by a.com (b.com) alone.
1100 | a.subsumes(aOrb) === true;
1101 | b.subsumes(aOrb) === true;
1102 |
1103 |
1109 | var aORbANDc = aORb.and(new Label("https://c.com");
1110 |
1111 |
1112 | Converting to string:
1113 |
1114 | empty.toString() === "'none'";
1115 | a.toString() === "https://a.com";
1116 | aANDb.toString() === "(https://a.com) AND (https://b.com)";
1117 | aORb.toString() === "https://a.com OR https://b.com";
1118 | aORbANDc.toString() === "(https://a.com OR https://b.com) AND (https://c.com)";
1119 |
1120 | 1152 | [Constructor, Exposed=(Window, Worker), SecureContext] 1153 | interface Privilege { 1154 | static Privilege FreshPrivilege(); // Named constructor 1155 | Label asLabel(); 1156 | 1157 | Privilege combine(Privilege other); 1158 | [Throws] Privilege delegate(Label label); 1159 | }; 1160 |1161 | 1162 |
internalLabel.and(otherLabel)
.
1209 |
1210 | SecurityError
1225 | exception and terminate this algorithm.
1226 |
1227 | 3. Else, return a new Privilege that has an
1228 | internal privilege label set to
1229 | label.
1230 |
1231 | https://example.com
has a privilege whose internal
1249 | privilege label is
1250 | Label("https://example.com")
.
1251 |
1252 | As a result, reading data that is sensitive to
1253 | Label("https://example.com")
does
1254 | not confine the context. For example, reading a
1255 | labeled object whose confidentiality label is
1256 | Label("https://example.com")
does
1257 | not restrict the context from communicating—and thus
1258 | accidentally leaking that object's contents—to another origin.
1259 | To prevent accidental leaks, the author should drop privileges by
1260 | setting the current privilege to an empty
1261 | privilege:
1262 |
1263 | // Save privilege in case we need it later:
1264 | var __savedPriv = COWL.privilege;
1265 |
1266 | // Drop privilege (set the context privilege to the empty privilege):
1267 | COWL.privilege = new Privilege();
1268 |
1269 |
1270 | After this point, if the context reads data with a
1271 | Label("https://example.com")
1272 | confidentiality label, COWL will restrict it to communicating
1273 | with https://example.com
.
1274 |
1282 | // Create new fresh privilege:
1283 | var priv = new FreshPrivilege();
1284 |
1285 | // Take ownership of the fresh privilege:
1286 | COWL.privilege = COWL.privilege.combine(priv);
1287 |
1288 | // Associate the unique label with the password:
1289 | var labeledPassword = new LabeledObject(password, {confidentiality: priv.asLabel()});
1290 |
1291 | // Send the labeled password to the checker iframe:
1292 | checker.postMessage(labeledPassword, "https://untrusted.com");
1293 |
1294 | Once the https://untrusted.com
cowl iframe reads the
1295 | password it will be tainted by the unique, internal
1296 | privilege label of priv
; the unique origin
1297 | ensures that it cannot send the password to, for example, public
1298 | parts of https://example.com
. Indeed, only the
1299 | owner of priv
can disseminate the labeled password
1300 | (result) arbitrarily.
1301 | https://university.edu
isolates
1307 | different parts of their site according to
1308 | users. For user1
it can do this as follows:
1309 |
1310 |
1311 | // Create a label corresponding to the university origin:
1312 | var uni = new Label(window.location.origin);
1313 | // Create a new label that corresponds to user1's data on university.edu:
1314 | var user1 = uni.or("app:user1"); // Here the app:user1 is an application-specific princial
1315 |
1316 | // Originally, COWL.privilege.asLabel().equals(uni).
1317 | // Drop the current context privilege to a delegated privilege:
1318 | COWL.privilege = COWL.privilege.delegate(user1);
1319 |
1320 |
1321 | At this point, the context can only arbitrarily disseminate data
1322 | that is labeled
1323 | Label("https://university.edu").or("app:user1")
;
1324 | it cannot disseminate data that is
1325 | sensitive to the university (e.g., which is labeled
1326 | Label("https://university.edu")
)
1327 | or to another user (e.g., user2
's data is
1328 | labeled
1329 | Label("https://university.edu").or("app:user2")
).
1330 | 1424 | [SecureContext, COWLContext] 1425 | interface COWL { 1426 | [SetterThrows] static attribute Label confidentiality; 1427 | [SetterThrows] static attribute Label integrity; 1428 | [SetterThrows] static attribute Privilege privilege; 1429 | }; 1430 |1431 | 1432 |
false
, throw a
1450 | SecurityError
exception and terminate this
1451 | algorithm.
1452 |
1453 | 4. Else, set the current confidentiality label to conf.
1454 |
1455 | false
,
1472 | throw a SecurityError
1473 | exception and terminate this algorithm.
1474 |
1475 | 4. Else, set the current integrity label to int.
1476 |
1477 |
1503 | COWL.integrity = new Label(window.location.origin);
1504 |
1505 | https://mashup.com
can set the context
1509 | confidentiality label to receive data sensitive from
1510 | https://provider.com
:
1511 |
1512 | COWL.confidentiality = new Label('https://provider.com');
1513 |
1514 | At this point, the context can only communicate with
1515 | https://provider.com
. The data provider can
1516 | ensure that only appropriately labeled contexts
1517 | can inspect an HTTP response by setting response labels using
1518 | the Sec-COWL
response
1519 | header.
1520 | send()
method for LabeledObject
1534 | arguments). It SHOULD be exposed to both confined contexts and
1535 | unconfined contexts.
1536 |
1537 | A LabeledObject MUST have an internal protected
1538 | object, a confidentiality label, and an
1539 | integrity label. The interface is defined below.
1540 |
1541 | 1542 | dictionary CILabel { 1543 | Label? confidentiality; 1544 | Label? integrity; 1545 | }; 1546 | 1547 | [Constructor(object obj, optional CILabel labels), Exposed=(Window, Worker), SecureContext] 1548 | interface LabeledObject { 1549 | readonly attribute Label confidentiality; 1550 | readonly attribute Label integrity; 1551 | 1552 | [GetterThrows] readonly attribute object protectedObject; 1553 | 1554 | [Throws] LabeledObject clone(optional CILabel labels); 1555 | }; 1556 |1557 | 1558 |
false
, the constructor
1587 | MUST throw a SecurityError
exception
1588 | and terminate this algorithm.
1589 |
1590 | 7. Else, the user agent MUST return a new
1591 | LabeledObject, with the protected object
1592 | set to obj clone, the confidentiality label set to
1593 | conf, and the integrity
1594 | label set to int.
1595 |
1596 | SecurityError
exception and terminnate this algorithm.
1634 |
1635 | 2. Invoke the context tainting algorithm with the LabeledObject's confidentiality and integrity labels.
1637 |
1638 | 3. If the tainting algorithm raised an exception, re-throw the
1639 | exception and terminate this algorithm.
1640 |
1641 | newConf.subsumes(conf, privs)
1707 | returns false
or
1708 | if int.subsumes(newInt, privs)
1709 | returns false
, the method
1710 | MUST throw a SecurityError
exception and
1711 | terminate this algorithm.
1712 |
1713 | Note, these checks ensure that the new labels of the object are
1714 | at least as restricting as the original labels, taking into
1715 | consideration the privileges of the context.
1716 |
1717 | 7. Else, return a new LabeledObject, with the
1718 | protected object set to obj, the
1719 | confidentiality label set to newConf, and the
1720 | integrity label set to newInt.
1721 |
1722 | https://police.gov
wishes to plot the
1737 | location of police cars on a map provided by
1738 | https://maps.biz
without revealing the individual
1739 | car locations (to the map provider). After revealing the general area to
1740 | https://maps.biz
, the author of
1741 | https://police.gov
labels the police car coordinates and
1742 | sends them to the mapping service:
1743 |
1744 |
1745 |
1746 | // Fetch map for provided location and draw it
1747 | mapsIframe.postMessage({ cmd: 'draw', location: ... }, mapsOrigin);
1748 |
1749 | var locations = ... // Array of police-car coordinates
1750 |
1751 | // Label the locations (as being sensitive to the police origin):
1752 | var labeledLocations = new LabeledObject(locations,
1753 | { confidentiality: new Label(window.location.origin) });
1754 |
1755 | // Send the labeled locations to the map iframe to plot them
1756 | mapsIframe.postMessage({ cmd: 'plot', locations: labeledLocations }, mapsOrigin);
1757 |
1758 |
1759 | When receiving a draw
message, the author of
1760 | https://maps.biz
navigates the iframe map (a nested
1761 | context) to draw the map; otherwise, it simply forwards messages
1762 | from the parent (e.g., plot
, zoom
, and
1763 | move
). (This design ensures that only the innermost
1764 | iframe gets tainted.)
1765 |
1766 | The innermost map cowl iframe registers a handler, that, for
1767 | example, draws cars on top of map tiles:
1768 |
1769 | window.addEventListener("message", function (event) {
1770 | switch (event.data.cmd) {
1771 | case 'plot':
1772 | var coordinates = event.data.locations.protectedObject;
1773 | coordinates.forEach(function (coordinate) {
1774 | // add car to map at coordinate
1775 | });
1776 | case 'zoom': ...
1777 | case 'move': ...
1778 | ...
1779 | };
1780 | }, false);
1781 |
1782 |
1783 | Note that before getting the first protectedObject, the iframe
1784 | can communicate arbitrarily, e.g., to fetch map tiles. But once
1785 | it inspects the confidential locations COWL confines the
1786 | code—it restricts it to only communicating with
1787 | https://police.gov
. Importantly, it can keep
1788 | receiving messages from its parent context via
1789 | postMessage() to, for instance, move a car.
1790 | https://example.com
wishes to ensure
1794 | that a particular JSON object conforms to a set of validation
1795 | filters before submitting it to a remote server. Consider, for
1796 | example, a form validator that checks if an email address is
1797 | valid. To this end, it labels the JSON and sends the labeled
1798 | object to a cowl iframe that performs the validation.
1799 |
1800 | The validation author inspects the object, but only endorses it
1801 | if it it is an email:
1802 |
1803 | function handler(lObj) {
1804 | if (isValidEmail(lObj.protectedObject)) {
1805 | var origin = window.location.origin;
1806 | // Endorse with application-specific label: ' OR app:isValidEmail'
1807 | var endorsement = new Label(origin).or('app:isValidEmail');
1808 | // Return a clone of the labeled object that is additionally
1809 | // endorsed by the validator's (sub-)origin.
1810 | return lObj.clone({ integrity: lObj.integrity.and(endorsement) });
1811 | } else {
1812 | return null;
1813 | }
1814 | }
1815 |
1816 | The author of https://example.com
can then pass the
1817 | endorsed object to other validators (e.g., a validator that checks emails
1818 | against a black-list) or end-point (e.g., a server), who can, in turn,
1819 | further endorse the object or verify the origins that have endorsed it.
1820 | Sec-COWL
HTTP Header FieldSec-COWL
HTTP header field is
1828 | used to convey label metadata.
1829 |
1830 | Label metadata is either labeled context metadata
1831 | or labeled data metadata.
1832 |
1833 | Labeled context metadata encodes COWL state
1834 | information, including:
1835 |
1836 | * the serialized context confidentiality label given by the ctx-confidentiality directive,
1837 | * the serialized context integrity label given by the ctx-integrity directive, and
1838 | * the serialized context privilege's internal privilege label given by the ctx-privilege directive.
1839 |
1840 | Its ABNF is:
1841 |
1842 | 1843 | ctx-metadata = ctx-directive *( ";" [ ctx-directive ] ) 1844 | ctx-directive = *WSP ctx-directive-name 1*WSP label-expression 1845 | ctx-directive-name = "ctx-confidentiality" / "ctx-integrity" / "ctx-privilege" 1846 |1847 | 1848 | Labeled data metadata is used to convey the 1849 | confidentiality and integrity labels of an HTTP request or response, using 1850 | the data-confidentiality and 1851 | data-integrity directives. Its ABNF is: 1852 | 1853 |
1854 | data-metadata = data-directive *( ";" [ data-directive ] ) 1855 | data-directive = *WSP data-directive-name 1*WSP label-expression 1856 | data-directive-name = "data-confidentiality" / "data-integrity" 1857 |1858 | 1859 | The ABNF for serialized labels is: 1860 | 1861 |
1862 | label-expression = empty-label / and-expression / or-expression / principal-expression 1863 | and-expression = *WSP "(" or-expression *WSP ")" *( 1*WSP "AND" WSP and-expression ) 1864 | or-expression = *WSP principal-expression *( 1*WSP "OR" WSP or-expression ) 1865 | empty-label = "'none'" 1866 |1867 | 1868 | Note, the ABNF grammar limits the expression of labels to conjunctions of 1869 | disjunctions, even though the JavaScript API allows AND's and OR's to be 1870 | used interchangably. This is done to simplify auditing of declarative 1871 | (header) labels, but may be changed in future versions to be more flexible. 1872 | 1873 | The ABNF for serialized principals is: 1874 |
1875 | principal-expression = origin-principal-expression 1876 | / app-principal-expression 1877 | / unique-principal-expression 1878 | origin-principal-expression = "'self'" / host-source 1879 | app-principal-expression = "app:" 1*( ALPHA / DIGIT / "-" ) 1880 | unique-principal-expression = "unique:" UUID 1881 |1882 | 1883 | 1884 | The parsing algorithms for label metadata are given in 1885 | [[#parse-labeled-metadata-data]] and [[#parse-labeled-metadata-ctx]]. 1886 | 1887 |
Sec-COWL
in HTTP RequestsSec-COWL
HTTP requests is:
1891 |
1892 | 1893 | "Sec-COWL:" ( ctx-metadata [ "," data-metadata ] ) / ( data-metadata [ "," ctx-metadata ] ) 1894 |1895 | 1896 | The user agent MUST send a header field named
Sec-COWL
1897 | along with requests if confinement mode is enabled and calling
1898 | determine request's referrer algorithm does not return
1899 | none
. The value of this header MUST contain the labeled
1900 | context metadata of the context that performed the request. This
1901 | labeled context metadata MUST include the current context
1902 | confidentiality label, context integrity label, and
1903 | context privileges. The user agent MAY send another header with
1904 | this field name whose value is labeled data metadata (e.g., when
1905 | sending labeled objects with XMLHttpRequest).
1906 |
1907 | Note, according to [[!RFC7231]], the user agent MAY combine
1908 | multiple header field values into a single, comma-separated
1909 | value.
1910 |
1911 |
1912 | https://a.com
page that has
1914 | read data sensitive to https://b.com
.
1915 |
1916 | Sec-COWL: ctx-confidentiality https://b.com;
1917 | ctx-integrity 'none';
1918 | ctx-privilege https://a.com
1919 |
1920 | https://university.edu
context that owns a
1924 | delegated privilege and a FreshPrivilege():
1925 |
1926 |
1927 | Sec-COWL: ctx-confidentiality 'none';
1928 | ctx-integrity 'none';
1929 | ctx-privilege (https://university.edu OR app:user1) AND (unique:a0281e1f-8412-4068-a7ed-e3f234d7fd5a)
1930 |
1931 | Sec-COWL
header that contains a ctx-metadata
1935 | directive to retrieve the labeled context metadata.
1936 | Similarly, a server SHOULD only use the first Sec-COWL
1937 | header that contains a data-metadata directive to retrieve
1938 | the labeled data metadata of the request.
1939 |
1940 | Sec-COWL
in HTTP ResponsesSec-COWL
HTTP responses is:
1946 | 1947 | "Sec-COWL:" ctx-metadata / data-metadata 1948 |1949 | 1950 | The header value may contain labeled context metadata 1951 | which can be used to set the initial COWL state of a 1952 | context; or it may contain labeled data metadata which 1953 | specifies the sensitivity of the response (which COWL then uses 1954 | to determine whether or not to block the response). 1955 | 1956 |
https://university.edu/~user1
page should run with
1959 | a delegated privilege—namely,
1960 | Label("https://university.edu/").or("app:user1")
—from
1961 | the start:
1962 |
1963 | Sec-COWL: ctx-privilege 'self' OR app:user1;
1964 |
1965 | https://a.com
may wish to respond to a
1968 | request with data that is sensitive to both https://a.com
and
1969 | https://b.com
, while simultaneously indicating that
1970 | it endorses the response data:
1971 |
1972 | Sec-COWL: data-confidentiality ('self') AND (https://b.com);
1973 | data-integrity 'self'
1974 |
1975 | COWL blocks the response unless the current context's labels
1976 | are at least as restricting.
1977 | send()
method:
1999 |
2000 | 2001 | partial interface XMLHttpRequest { 2002 | void send(LabeledObject lobj); 2003 | }; 2004 |2005 | The send(lobj) method MUST use an 2007 | algorithm that is equivalent to the following: 2008 | 2009 | 1. Let obj be the protected object of the 2010 | lobj argument. 2011 | 2012 | 2. Let conf be the confidentiality label of the lobj argument. 2014 | 2015 | 3. Let int be the integrity label of the lobj argument. 2017 | 2018 | 4. Let privs be the current context privileges. 2019 | 2020 | 5. Let remoteConf be the label 2021 | returned by the Label(principal) constructor 2022 | called with the stringified origin 2023 | url 2024 | associated with the request. 2025 | 2026 | 6. If
remoteConf.subsumes(conf,
2027 | privs)
returns false
, throw
2028 | a SecurityError
and terminate this algorithm.
2029 |
2030 | The user agent SHOULD report a warning that the context
2031 | attempted to leak data to a remote server.
2032 |
2033 | 7. Let json be a new JSON object with the
2034 | following entries:
2035 |
2036 | * "confidentiality"
set to the serialized conf.
2037 | * "integrity"
set to the the serialized int.
2038 | * "object"
set to obj.
2039 |
2040 | 8. Set the Content-Type
header to `application/labeled-json`
.
2041 |
2042 | 9. Append a header named Sec-COWL
to the
2043 | author request headers associated with the object this
2044 | methods was called on. The value of the
2045 | Sec-COWL
header MUST be labeled data
2046 | metadata containing the confidentiality and integrity
2047 | labels of the lobj argument.
2048 |
2049 | 10. Invoke the send()
2050 | method on the object this method was called on with
2051 | json as an argument.
2052 |
2053 | Note, that send()
2054 | throws an exception if obj cannot be serialized.
2055 | User agents MUST ensure that all protected objects can be
2056 | serialized at the time of creating LabeledObjects.
2057 |
2058 | https://example.com
sends JSON object
2071 | endorsed by https://validator.com
:
2072 |
2073 | // Suppose that labeledObject is a public, high-integrity value:
2074 | labeledObject.confidentiality.toString() === "'none'";
2075 | labeledObject.integrity.toString() === "https://validator.com";
2076 |
2077 | // Create an XHR request:
2078 | var req = new XMLHttpRequest()
2079 | req.open("POST", "https://example.com/");
2080 |
2081 | // Send the labeled object:
2082 | req.send(labeledObject);
2083 |
2084 | Assuming the context has a default COWL state,
2085 | send()
would make an HTTP request of the form:
2086 |
2087 |
2088 | Sec-COWL: ctx-confidentiality 'none';
2089 | ctx-integrity 'none';
2090 | ctx-privilege https://example.com
2091 | Sec-COWL: data-confidentiality 'none';
2092 | data-integrity https://validator.com
2093 | Content-Type: application/labeled-json;
2094 |
2095 | {
2096 | "confidentiality": "'none'",
2097 | "integrity": "https://validator.com",
2098 | "object": ... JSON object ...
2099 | }
2100 |
2101 | The server can then verify the integrity label of the request
2102 | and ensure that, if the user agent is conformant, the data was
2103 | endorsed by https://validator.com
.
2104 | XMLHttpRequestResponseType
2112 | enumeration is extended with a new response type:
2113 |
2114 | 2115 | enum XMLHttpRequestResponseType { 2116 | // ... existing response types ... 2117 | "labeled-json" 2118 | }; 2119 |2120 | 2121 | 2. The Response 2122 | body section of the specification is modified to add: 2123 | 2124 | 1. An XMLHttpRequest has associated 2125 | response LabeledObject object. 2126 | 2127 | 2. A labeled JSON response is the return value 2128 | of these steps: 2129 | 2130 | 1. If the response LabeledObject object is non-null, return it. 2131 | 2132 | 2. If responseType is not 2133 |
"labeled-json"
or the final MIME
2134 | type is not application/labeled-json
, return null.
2135 |
2136 | 3. Let bytes be the response's body.
2137 |
2138 | 3. If bytes is null, return null.
2139 |
2140 | 4. Let JSON text be the result of running
2141 | utf-8 decode on byte stream bytes.
2142 |
2143 | 5. Let JSON object be the result of invoking the initial
2144 | value of the parse
property of the
2145 | JSON
object, with
2146 | JSON text as its only argument. If that threw an
2147 | exception, return null. [[!ECMA-262]]
2148 |
2149 | 6. If the JSON object is missing any of
2150 | the three entries: "object"
,
2151 | "confidentiality"
, or
2152 | "integrity"
return null.
2153 |
2154 | 7. Let protected object be the value of the "object"
entry.
2155 |
2156 | 8. Let self be the
2157 | url
2158 | associated with the response.
2159 |
2160 | 9. Let conf be the label returned by invoking the
2161 | [[#parse-label-expression]] algorithm with the
2162 | "confidentiality"
entry of the JSON object
2163 | and self. If parsing failed, return null.
2164 |
2165 | 10. Let int be the label returned by invoking the
2166 | [[#parse-label-expression]] algorithm with the
2167 | "integrity"
entry of the JSON object
2168 | and self. If parsing failed, return null.
2169 |
2170 | 11. Let responseInt be the label
2171 | returned by the Label(principal) constructor
2172 | called with self.
2173 |
2174 | 12. If responseInt does not subsume
2175 | int, return null.
2176 |
2177 | The user agent SHOULD report a warning message indicating that
2178 | the server provided an integrity label that it is not allowed
2179 | to provide.
2180 |
2181 | 13. Set the response LabeledObject object to a newly
2182 | created LabeledObject whose
2183 | protected object is protected object,
2184 | whose confidentiality label is conf, and
2185 | whose integrity label is int.
2186 |
2187 | 14. Return the response LabeledObject object.
2188 |
2189 | 3. Modify the response attribute by
2190 | adding the following clause to step 2 of the
2191 | ↪ Otherwise clause:
2192 |
2193 | ↪ If responseType is "labeled-json"
2194 | Return the labeled JSON response.
2195 | 2196 | 4. Modify step 12 of the open() method by adding the 2197 | following sub-step: 2198 | 2199 | * Set response LabeledObject object to null. 2200 | 2201 | 2202 |https://provider.com
uses a CORS response header to
2213 | send https://mashup.com
a labeled JSON object. To
2214 | ensure that the data is protected it sets the
2215 | Content-Type
response header value to
2216 | `application/labeled-json`
and sets the labels
2217 | appropriately:
2218 |
2219 |
2220 | Access-Control-Allow-Origin: https://mashup.com
2221 | Content-Type: application/labeled-json;
2222 |
2223 | {
2224 | "confidentiality": "'self'",
2225 | "integrity": "'self'",
2226 | "object": ... JSON object ...
2227 | }
2228 |
2229 |
2230 | The confidentiality label specifies that the object is
2231 | confidential to https://provider.com
and should
2232 | not be disseminated arbitrarily.
2233 |
2234 | Note, the server operator can also set a Sec-COWL
response header if it
2236 | wished to ensure that the context is already confined according to some
2237 | label.
2238 |
2239 | The author of https://mashup.com
can read such
2240 | labeled responses by simply setting the responseType accordingly:
2242 |
2243 |
2244 | // Create an XHR request to get the data:
2245 | var req = new XMLHttpRequest()
2246 | req.open("GET", "https://provider.com/apis/...");
2247 | req.responseType = "labeled-json";
2248 | req.onload = function (e) {
2249 | var labeledObject = req.response; // is a LabeledObject
2250 |
2251 | // At this point, the context is still untainted, but:
2252 | labeledObject.confidentiality.toString() === "https://provider.com";
2253 | labeledObject.integrity.toString() === "https://provider.com";
2254 | };
2255 | req.send();
2256 |
2257 |
2258 | Here, COWL sets the response to a new LabeledObject, but does not taint the
2260 | context with the response label. Indeed the
2261 | https://mashup.com
integrator can perform many
2262 | other requests to different origins. Only when the
2263 | protected objects of these labeled objects are
2264 | used will COWL taint the context and impose the label
2265 | restrictions.
2266 |
2271 | Access-Control-Allow-Origin: *
2272 | Content-Type: application/labeled-json;
2273 |
2274 | {
2275 | "confidentiality": app:too-secret,
2276 | "integrity": 'none',
2277 | "object": ... base64-encoded image ...
2278 | }
2279 |
2280 | Once the receiver inspects the protectedObject
2281 | of the response, COWL taints the context and ensures that
2282 | it cannot communicate with anybody.
2283 |
2284 | Note, to read the protected object, one must do so in a confined context
2285 | since reading such a sensitive data would prevent code from communicating
2286 | arbitrarily thereafter.
2287 | Document
of the
2342 | Window
object on which the
2343 | method was invoked.
2344 |
2345 | 4. Let dstConf be the Label returned
2346 | by the label upgrade algorithm when invoked with the
2347 | dstState context confidentiality label and
2348 | context privilege.
2349 |
2350 | Note, in using the label upgrade COWL flexibly assumes
2351 | that receivers wish to receive data potentially sensitive to
2352 | their origin (really data they can declassify with their
2353 | privileges).
2354 |
2355 | 5. Let dstInt be the dstState context
2356 | integrity label.
2357 |
2358 | 6. If dstConf does not subsume
2359 | conf or if int does not
2360 | subsume dstInt, then abort the
2361 | remaining steps silently.
2362 |
2363 | 2. Perform the following step between step 9 and 10 in the
2364 | MessagePort postMessage()
method:
2365 |
2366 | 1. Let conf be the current context's effective
2367 | confidentiality label.
2368 |
2369 | 2. Let int be the current context's effective
2370 | integrity label.
2371 |
2372 | 3. Let dstState be the COWL state
2373 | associated with the owner of the target
2374 | port
the Message Port
2375 | postMessage()
was called on.
2376 |
2377 | 4. Else, let dstConf be the Label
2378 | returned by the label upgrade algorithm when invoked with
2379 | the dstState context confidentiality label and
2380 | context privilege.
2381 |
2382 | Note, in using the label upgrade COWL flexibly assumes
2383 | that receivers wish to receive data potentially sensitive to
2384 | their origin (really data they can declassify with their
2385 | privileges).
2386 |
2387 | 5. Let dstInt be the dstState context
2388 | integrity label.
2389 |
2390 | 6. If dstConf does not subsume
2391 | conf or if int does not
2392 | subsume dstInt, then abort the
2393 | remaining steps.
2394 |
2395 | cowl
attribute:
2404 |
2405 | 2406 | partial interface HTMLIFrameElement { 2407 | attribute boolean cowl; 2408 | }; 2409 |2410 | 2411 | The cowl attribute, when 2412 | specified, enables a set of extra restrictions on any content 2413 | hosted by the iframe, much like the sandbox attribute. 2414 | We call an iframe with the cowl attribute set 2415 | a cowl iframe. 2416 | 2417 | When an iframe element with a cowl 2418 | attribute has its nested browsing context created (before 2419 | the initial
about:blank
Document is created), the
2420 | user agent MUST enable the context's confinement mode. The
2421 | iframe element's cowl attribute may not
2422 | be set or changed after this point.
2423 |
2424 | ISSUE: what's the best way to deal with authors modifying the cowl attribute?
2425 |
2426 | 1. When confinement mode is enabled the user agent MUST
2427 | ensure that content cannot access other content from the
2428 | same origin (e.g., using an iframe's
2429 | contentDocument
) or use
2430 | otherwise unsafe APIs that would violate label restrictions.
2431 |
2432 | Specifically, if a browsing context's confinement
2433 | mode is enabled the user agent MUST set the following flags
2434 | of the context's active sandboxing flag set:
2435 |
2436 | * The sandboxed document.domain
browsing context flag.
2437 |
2438 | * The sandboxed origin browsing context flag.
2439 |
2440 | Note, this flag is used to ensure that the browsing context cannot
2441 | directly access content of the same origin. It also prevents
2442 | script from reading from or writing to the
2443 | document.cookie
IDL attribute, and blocks access
2444 | to localStorage
. [[WEBSTORAGE]]
2445 |
2446 | ISSUE: is there a better way for us to just disallow direct DOM
2447 | access and preserve same-origin?
2448 |
2449 | * The sandboxed navigation browsing context flag.
2450 |
2451 | * The sandboxed auxiliary navigation browsing context flag.
2452 |
2453 | * The sandboxed top-level navigation browsing context flag.
2454 |
2455 | In addition to these flags, the user agent MUST set a
2456 | container policy of the cowl iframe that contains
2457 | the confined context that disables the following features:
2458 |
2459 | * Workers: Web Workers, Shared Workers, Service Workers
2460 |
2461 | * Communication: Web Sockets, Server-sent events,
2462 | channel messaging, broadcast channels and WebRTC
2463 |
2464 | * Storage: document.cookie, localStorage, IndexedDB
2465 |
2466 |
2467 |
2512 | var a = Label("https://a.com"); // https://a.com
2513 | var aORb = Label("https://a.com").or("https://b.com"); // https://a.com OR https://b.com
2514 | var a2 = a.and(aORb); // (https://a.com) AND (https://a.com OR https://b.com) ≡ https://a.com
2515 |
2516 |
2517 | The label a2
is equivalent to a
(since
2518 | a.subsumes(aORb)
):
2519 |
2520 |
2521 | a2.toString() === "https://a.com";
2522 | a2.equals(a);
2523 |
2524 | true
.
2538 |
2539 | 2. Else, return false
.
2540 |
2541 | Note, when interpreting labels as mathematical formulae, label
2542 | subsumption is logical implication: A
2543 | subsumes B is equivalent as A
2544 | implies B, i.e, A⇒B.
2545 | label.and(privLabel)
.
2584 |
2585 | Note, label upgrade is the dual of label downgrade.
2586 | This can be used to safely endorse data labeled label
2587 | (and thus potentially already endorsed).
2588 | SecurityError
exception. The user agent MUST use an algorithm
2598 | whose behavior is as follows:
2599 |
2600 | 1. Let currentConf be the current context
2601 | confidentiality label.
2602 |
2603 | 2. Let currentInt be the current context
2604 | integrity label.
2605 |
2606 | 2. Let newConf be the Label returned by the by
2607 | the label downgrade algorithm when invoked with
2608 | currentConf.and(confidentiality)
2609 | and current privilege.
2610 |
2611 | 3. Let newInt be the Label returned by the
2612 | label downgrade algorithm when invoked with
2613 | currentInt.or(integrity) and current
2614 | privilege.
2615 |
2616 | 4. If the current context is an unconfined context and
2617 | either newConf or newInt are not the
2618 | empty label, throw a SecurityError
exception
2619 | and terminate this algorithm.
2620 |
2621 | 5. Set the context confidentiality label to newConf.
2622 |
2623 | 6. Set the context integrity label to newInt.
2624 |
2625 | true
if the current context is allowed to write to
2632 | (or create) an entity labeled as such; otherwise, it returns
2633 | false
. The user agent MUST use an algorithm whose
2634 | behavior is as follows:
2635 |
2636 | 1. Let currentConf be the current context's effective
2637 | confidentiality label.
2638 |
2639 | 2. Let currentInt be the current context's effective
2640 | integrity label.
2641 |
2642 | 3. If objConf does not subsume
2643 | currentConf or if currentInt does not
2644 | subsume objInt, return false
.
2645 |
2646 | 7. Else, return true
.
2647 | null
.
2667 |
2668 | 2. Else, let output be a newly constructed
2669 | Privilege object with the same internal privilege
2670 | label as that of input.
2671 |
2672 |
2673 | null
if the privilege subsumes any privilege
2679 | corresponding to an origin principal.
2680 | Sec-COWL
request header
2752 | to ensure untrustworthy data does not affect the computation in an
2753 | unsafe way.
2754 | Sec-COWL
, return allowed and
2783 | terminate this algorithm.
2784 |
2785 | 2. Let destination be the request's destination.
2786 |
2787 | 3. Let context be the client
2788 | associated with the request.
2789 |
2790 | 4. If context is null, let context be the
2791 | incumbent settings object.
2792 |
2793 | Note, the client associated with the
2794 | request is null when navigating, so we use
2795 | the incumbent settings object to get or set the COWL
2796 | state of the browsing context that initiated the request.
2797 |
2798 | 5. Let state be the COWL state retrieved via the
2799 | environment settings object context.
2800 |
2801 | 6. Let metadata be
2802 | the first header whose name
2803 | is Sec-COWL
in the response's header list.
2804 |
2805 | 7. If destination is "document"
,
2806 | "worker"
or "serviceworker"
:
2807 |
2808 | 0. Let self be the serialization of the
2809 | origin retrieved via the
2810 | environment settings object context.
2811 |
2812 | 1. Let conf, int, priv be the
2813 | result of calling the parse labeled context
2814 | metadata algorithm with metadata and self.
2815 |
2816 | 2. If either conf, int, or
2817 | priv are null, return blocked.
2818 |
2819 | The user agent SHOULD report a warning that the server supplied a
2820 | malformed Sec-COWL
header.
2821 |
2822 | 3. Else:
2823 |
2824 | 1. If context is an unconfined context,
2825 | return blocked and terminate this algorithm.
2826 |
2827 | The user agent SHOULD report a warning that the application
2828 | attempted to embed confined content outside a cowl iframe.
2829 |
2830 | 2. If priv is not a delegated privilege of the
2831 | state context privilege, return
2832 | blocked and terminate this algorithm.
2833 |
2834 | The user agent SHOULD report a warning that the server supplied
2835 | a privilege that it is not trusted for.
2836 |
2837 | 3. If the state effective integrity label does
2838 | not subsume int, return blocked
2839 | and terminate this algorithm.
2840 |
2841 | The user agent SHOULD report a warning that the server supplied
2842 | an integrity label that it is not trusted for.
2843 |
2844 | 4. Set the state context confidentiality label
2845 | to conf.
2846 |
2847 | 5. Set the state context integrity label
2848 | to int.
2849 |
2850 | Note, by performing the label subsumption check (step 3
2851 | above) before setting the context privilege (next step),
2852 | the context integrity label can be upgraded from the
2853 | empty label, while allowing the context privilege
2854 | to also be dropped.
2855 |
2856 | 6. Set the state context privilege to
2857 | priv.
2858 |
2859 | 7. Return allowed.
2860 |
2861 | 8. Else:
2862 |
2863 | 1. Let self be the origin of the
2864 | url
2865 | associated with the response.
2866 |
2867 | 2. Let conf and int be the
2868 | results of calling the parse labeled data metadata
2869 | with metadata and self.
2870 |
2871 | 3. If either conf or int is null,
2872 | return blocked and terminate this algorithm.
2873 |
2874 | The user agent SHOULD report a warning that the server supplied a
2875 | malformed Sec-COWL
header.
2876 |
2877 | 4. Let effConf be the Label returned by the
2878 | label downgrade algorithm when invoked with conf and
2879 | the state current privilege.
2880 |
2881 | Note, effConf is the effective confidentiality label of
2882 | the response—COWL flexibly declassifies responses (as much as
2883 | the current privilege permits).
2884 |
2885 | 5. If the state context confidentiality
2886 | label subsumes effConf and int
2887 | subsumes the state effective integrity
2888 | label, return allowed.
2889 |
2890 | 6. Else, return blocked.
2891 |
2892 | Note, COWL conservatively blocks a response that is
2893 | potentially more confidential or less trustworthy than the
2894 | context making the request. In future versions of COWL,
2895 | certain responses (e.g., images) which are only not as
2896 | trustworthy as the context integrity label
2897 | may be allowed by the user agent.
2898 | ;
):
2916 |
2917 | 1. Skip whitespace.
2918 |
2919 | 2. Collect a sequence of characters that are
2920 | not space characters. The collected characters
2921 | are the directive name.
2922 |
2923 | 3. If there are characters remaining in token,
2924 | skip ahead exactly one character (which must be a
2925 | space character).
2926 |
2927 | 4. The remaining characters in token (if any) are
2928 | the directive value.
2929 |
2930 | 5. Let label value be the label returned by
2931 | invoking the parse label-expression algorithm with the
2932 | directive value and self. If parsing
2933 | failed, terminate the rest of these sub-steps.
2934 |
2935 | The user agent SHOULD report a warning that the server provided a
2936 | malformed label directive.
2937 |
2938 | 6. If directive name is data-confidentiality
2939 | and conf is null, let conf be label
2940 | value.
2941 |
2942 | 7. Else, if directive name is
2943 | data-integrity
and int is null,
2944 | let int be label value.
2945 |
2946 | 8. Else, terminate the rest of these sub-steps.
2947 |
2948 | The user agent SHOULD report a warning that the server provided a
2949 | malformed label directive.
2950 | 4. Return conf and int.
2951 |
2952 | ;
):
2976 |
2977 | 1. Skip whitespace.
2978 |
2979 | 2. Collect a sequence of characters that are
2980 | not space characters. The collected characters
2981 | are the directive name.
2982 |
2983 | 3. If there are characters remaining in token,
2984 | skip ahead exactly one character (which must be a
2985 | space character).
2986 |
2987 | 4. The remaining characters in token (if any) are
2988 | the directive value.
2989 |
2990 | 5. Let label value be the label returned by
2991 | invoking the parse label-expression algorithm with the
2992 | directive value and self. If parsing
2993 | failed, terminate the rest of these sub-steps.
2994 |
2995 | The user agent SHOULD report a warning that the server provided a
2996 | malformed label directive.
2997 |
2998 | 6. If directive name is
2999 | ctx-confidentiality
and conf is
3000 | null, let conf be label value.
3001 |
3002 | 7. Else, if directive name is
3003 | ctx-integrity
and int is null,
3004 | let int be label value.
3005 |
3006 | 8. Else, if directive name is
3007 | ctx-privilege
and priv is null,
3008 | let priv be a newly created privilege whose
3009 | internal privilege label is set to label
3010 | value.
3011 |
3012 | 9. Else, terminate the rest of these sub-steps.
3013 |
3014 | The user agent SHOULD report a warning that the server provided a
3015 | malformed label directive.
3016 |
3017 | 5. Return conf, int, and priv.
3018 |
3019 | orExp.or(prin)
,
3088 | if calling the or
method didn't throw an exception. Otherwise, fail.
3089 |
3090 | Note, the method throws an exception if prin is not a
3091 | principal. Hence, this algorithm should fail.
3092 |
3093 | 8. Set label to label.and(orExp)
.
3094 |
3095 | 8. Return label.
3096 |
3097 | When a user agent has to split a string on particular
3098 | delimiter characters delimiter, it MUST use an algorithm
3099 | equivalent to the following:
3100 |
3101 | 1. Let input be the string being split.
3102 |
3103 | 2. Let position be a pointer into input, initially
3104 | pointing at the start of the string.
3105 |
3106 | 3. Let delimiter length be the number of characters in delimiter.
3107 |
3108 | 4. Let tokens be the resulting list of tokens.
3109 |
3110 | 5. Strip leading and trailing whitespace.
3111 |
3112 | 6. If input is the empty string, fail.
3113 |
3114 | 7. While position is not past the end of input:
3115 |
3116 | 1. Let s be the empty string.
3117 |
3118 | 2. Skip whitespace.
3119 |
3120 | 3. Let done be `false`.
3121 |
3122 | 4. While done is `false` and position is not past
3123 | the end of input:
3124 |
3125 | 1. Collect a sequence of characters that are not space
3126 | characters. Append the resulting sequence to s.
3127 |
3128 | 3. If position points past the end of input,
3129 | set done to `true` and end these sub-steps.
3130 |
3131 | 4. Collect a sequence of characters that are space
3132 | characters. Let ws be the resulting sequence.
3133 |
3134 | 5. If the next delimiter length characters are an
3135 | ASCII case-insensitive match for the string
3136 | delimiter, then set done to `true` and advance
3137 | position by delimiter length characters.
3138 |
3139 | 6. Else, append ws to s.
3140 |
3141 | 5. Append s to tokens.
3142 |
3143 | 8. Return tokens.
3144 |
3145 | Sec-COWL
HTTP Header Field
3163 | application/labeled-json
MIME media type
3186 | application/json
. [[JSON]]application/json
. [[JSON]]application/json
. [[JSON]]application/json
. [[JSON]]application/json
. [[JSON]]application/labeled-json
3213 | type asserts that the resource is a JSON text that consists of an
3214 | object with a single entry called "confidentiality"
3215 | consisting of a string, a single entry called "integrity"
3216 | consisting of string, and a single entry called "object"
3217 | consisting of a JSON object. The relevant specifications are the
3218 | JSON specification and this specification. [[JSON]]
3219 |