Why the web needs finer-grained origins

Hey folks,

One of the long-standing topics of discussion in this group is the idea of
suborigins, and the question of how they might help web authors build more
secure applications.

There have been some lucid arguments for the utility of the concept in the
past, which I tried to distill down to a few core ideas (somewhat
independent of the current spec / Chrome prototype) with the hope that it
might help with the discussion next week:

https://docs.google.com/document/d/1sDVPKmhAdS4aioEguOUOneQkrHoXvZrHUEJEJuWYETI/edit

If you have time before TPAC, please take a look and feel free to leave
comments on the doc. I'm also pasting the text below for easier reading.

Have a good weekend,
-Artur

***

1. Introduction

It is a great irony of the web platform that its most damaging
vulnerability --
XSS, which allows full code execution in the context of the vulnerable
origin
-- is also the easiest one for developers to introduce. In complex
application
ecosystems XSS can dwarf other bug classes by an order of magnitude [1,2];
this
happens in large part because the most common programming tasks, including
displaying text or navigating to another page [3], lead to a vulnerability
unless special care is taken to properly handle any untrusted data used in
such
a context [4].

Historically, one of the most effective approaches for containing
potentially
vulnerable code in complex applications has been privilege separation, or,
reducing the amount of highly trusted code in the system. Focus on such
separation has been a common theme among engineers building some of the
Internet's most security-critical infrastructure including Bernstein's
lessons
from developing qmail [5], Provos' privilege separated OpenSSH [6], and the
multi-process architecture [7] and ongoing efforts to implement site
isolation
in Chrome [8].

Like other complex software, modern web applications are often composed of a
large number of components developed by dozens of engineers, including
third-party libraries written by hundreds of contributors; it is not
uncommon
for a web application codebase to comprise of millions of lines of code and
dependencies [9]. Currently, all such code executes with the full
privileges of
the hosting origin, and so an XSS bug in any part of the application has
complete access to all data and functionality available to scripts in that
origin. Due to the linkability inherent to the web, any such flaw can be
exploited against authenticated users by pointing them at a vulnerable
endpoint.

There is no compelling reason to allow each line of code executing in a
given
origin to access all resources available across that origin. The current
lack
of security separation in web applications is purely a legacy consequence of
developers needing a place under which to map their HTML pages and code,
combined with the absence of a way to isolate application components.

Suborigins [10, 11] address this problem by providing authors with a
lightweight mechanism to separate web application code into individual
components, while allowing each component to operate with minimal
disruption.
In this model, endpoints hosted in suborigins could only be affected by XSS
vulnerabilities in the same component, rather than by bugs elsewhere in the
origin.

2. Use cases of suborigins

In practice, even moderately complex web applications include many different
components, including:

- Application-specific endpoints corresponding to functionality visible to
  users. For example, a bulletin board application will typically have a
listing of posts, a comment thread for each post, a page to compose a new
message, an interface allowing users to log in, and a settings page to
manage
the user's profile information.

- Server-side modules to handle common important actions, e.g. login via
OAuth
  or single sign-on mechanisms [12], or allowing users to upload files [13].
Such modules typically require mapping to the application's URL hierarchy
upon
installation.

- RESTful API endpoints serving data needed to dynamically construct parts
of
  the UI, often returning data formatted as JSON [14] or XML, or even
partial
HTML templates fetched from the server as a response to a user action and
subsequently rendered to the user.

- Administrative interfaces accessible to application owners or superusers,
  e.g.  the /admin UI on a blog [15]. Large organizations frequently also
expose debugging and other application management endpoints, restricted to
developers and internal users.

- Client-side modules to allow developers to use widgets such as rich text
  editors [16] or to embed online ads [17]. This typically requires
unpacking
third-party code, including scripts and HTML, and hosting it in the
application's origin.

- Static HTML pages such as a product overview, privacy policy, terms of
  service page, a marketing subsite encouraging users to sign up for the
application, or other auxiliary content. In ecosystems where legacy HTML
pages
are co-hosted alongside authenticated applications, DOM XSS bugs in static
content can provide an effective avenue for attacks on sensitive user data
[18].

- Exception responses triggered by various error conditions (invalid request
  parameters, lack of access to a given resource, etc).

Such application components are generally independent from one another, may
be
written using different languages and development frameworks, and --
particularly in the case of functionality tangential to the main purpose of
the
application -- often include legacy code incompatible with modern security
best
practices.

More worryingly, even seemingly simple content can pose a disproportionate
risk
of introducing XSS bugs: error pages typically eschew the protections of
HTML
template systems and generate responses by concatenating strings (a common
anti-pattern responsible for XSS), and static HTML pages tend to load
complex
JS libraries, allowing client-side logic to introduce hard to spot DOM XSS
vulnerabilities [19].

The key insight of suborigins is that such application components are
logically
distinct enough that developers may often separate them into different
security
principals without affecting existing functionality. In particular, in this
model high-value endpoints could be isolated to protect the most sensitive
parts of the application, without requiring changes to unrelated code
exposed
in the origin.

3. Goals of suborigins

The sole objective of suborigins is to provide privilege separation between
client-side code executing in the same web origin to protect sensitive
functionality from XSS vulnerabilities in unrelated components.

To achieve this, a complementary design goal for suborigins is a focus on
facilitating adoption in web applications. As a defense-in-depth mechanism,
suborigins must be readily deployable in a number of scenarios to ensure
their
use by web authors and security engineers.

While a discussion of the full implications of these considerations is out
of
scope here, they influence the design goals of suborigins in two important
ways:

- Suborigins should provide "bi-directional" protection so that they can be
  used to isolate an application in a suborigin from malicious scripts in
the
main origin. In contrast to other features such as sandbox frames or CSP
this
allows protecting sensitive parts of the application instead of attempting
to
enumerate and contain all potential sources of maliciously injected code in
the
origin.

- Adopting suborigins should require minimal changes to application code
and,
  in common scenarios, be possible solely by adding server-side middleware
to
set the Suborigin header and properly handle any CORS requests from the same
suborigin. This means that suborigins should be able to obtain access to
some
capabilities granted to the origin as long as this doesn't conflict with the
goal of protecting the user from XSS. For example, in some cases a suborigin
may need to be able to inherit browser permissions (e.g. for location or
fullscreen access) or origin state, such as cookies, if allowed by the
developer in the suborigin configuration [20].

It is worth noting that the threat model of suborigins explicitly excludes
other security risks such as compromise of the server-side infrastructure,
malicious insiders, or other web security flaws such as CSRF or
clickjacking.

4. Shortcomings of current web isolation mechanisms

The draft of the suborigin specification offers a detailed discussion [21]
of
the difficulties of using sandbox iframes and hostname separation as a
mechanism to isolate code; we offer only a short summary of the main issues.

Sandbox iframes -- or sandbox documents, if the designation is delivered in
a
CSP header -- are meant to contain wholly untrusted content and isolate it
from
the rest of the hosting origin. They disable some crucial web features
(client-side storage, permissions), make it purposefully difficult to
communicate with other content in the same origin (they send CORS and
postMessage as the "null" origin), and propagate sandbox restrictions to all
sub-frames and newly opened windows. Perhaps most importantly, they require
placing all potentially untrusted, less-privileged or vulnerable content in
sandboxes, which is not practical for most applications.

An alternative approach of splitting a web application across multiple
hostnames can be tantalizing, but it runs into several practical problems
which
prevent deployment in most common scenarios. This includes the necessity to
create additional subdomains and re-organize the application so that each
logical component is only accessible on its own subdomain, leading to an
inflexible design intrinsically tied to the domain's DNS structure.
Migration
of application code is hindered by the lack of access to existing cookies
and
origin permissions, inability to use common origin-based APIs (e.g.
history.pushState) and incompatibility with existing links and bookmarks.
Most
damningly, even if a developer can separate her application into functional
components and expose them in subdomains, each subdomain will likely still
have
to host many unrelated features (error handlers, SSO endpoints, static
content
belonging to the given part of the application), reducing the security
value of
such a scheme. The paucity of existing examples of applications which split
their core components across hostnames is evidence of the difficulty of
employing this approach.

Content Security Policy, currently the most prominent XSS mitigation, is
largely complementary to suborigins: it may prevent code execution in the
event
of a markup injection flaw, but it doesn't protect an application from
vulnerabilities in other components exposed in the same origin.

Existing platform mechanisms are useful purpose-specific tools, but they
stop
short of enabling generic privilege separation on the web.

5. Parting thoughts

Suborigins are fairly uniquely positioned to allow web authors to introduce
meaningful protections against XSS in their applications. They introduce an
implementation of privilege separation to the web platform, while providing
allowances for real-world behaviors which reduce the restrictions on
existing
applications without eliminating the security benefits.

We believe that implementing suborigins as a first-class feature of the web
platform can add an important missing primitive and have a positive
long-term
impact on the state of web security.

6. References

[1] https://twitter.com/jvehent/status/911192609699373056

[2] Google VRP data from 2012-2016 shows XSS to be >60% of high-risk bugs,
amounting to 100+ XSS issues in sensitive origins each year.

[3] Markup injection and navigational XSS (via javascript: URIs) are two
major
root causes of XSS on the web. Common sources of XSS have historically
included
the following patterns:
- Generating server-side responses without properly escaping untrusted data.
- Generating markup on the client-side and passing it to an unsafe DOM API
  (Element.innerHTML, document.write, and their library wrappers such as
jQuery.html) without properly escaping untrusted data.
- Allowing navigation to untrusted URLs when generating links in server-side
  code (<a href="javascript:evil()">) or when performing client-side
navigations using an unsafe DOM API (window.open, location.href = ...).
- Removing default escaping applied by the framework or template system,
  usually under a mistaken assumption that data is fully application
controlled
(Django |safe, Closure templates |noAutoescape, bypassSecurityTrustAs*
functions in Angular)
- Applying escaping incorrectly (e.g. HTML-escaping in a JS inline event
  handler), or forgetting to escape all metacharacters required by a given
document context.
- Use of untrusted data in calls or assignments to any of the ~50 other DOM
  execution sinks, e.g. setTimeout, eval, ScriptElement.src,
FormElement.action, etc.

Most server-side frameworks and JavaScript libraries also introduce their
own
APIs which result in unsafe behaviors equivalent to the patterns above.

[4] There are some successful examples of web applications which have
significantly reduced their susceptibility to XSS, including Facebook,
GitHub
and users of certain internal frameworks at Google. They have usually done
so
by imposing constraints on web application authors to prevent them from
using
unsafe native web APIs, e.g. requiring them to use safe wrappers around
dangerous DOM methods, and allowing server-side responses to be generated
only
by template systems which guarantee proper escaping of interpolated data. In
all cases this required building layers of technical and process safeguards
(e.g. enforcing the compilation of all JS and preventing the use of
dangerous
methods; requiring security code reviews to allow exceptions) to work around
the inherent unsafety of the web platform.

[5] https://cr.yp.to/qmail/qmailsec-20071101.pdf, Sections 2.3, 5.1-4

[6] http://www.citi.umich.edu/u/provos/ssh/privsep.html
http://www.citi.umich.edu/u/provos/papers/privsep.pdf, Section 3

[7]
https://www.chromium.org/developers/design-documents/multi-process-architecture

[8] https://www.chromium.org/developers/design-documents/site-isolation

[9] https://www.wired.com/2015/09/google-2-billion-lines-codeand-one-place/

[10] https://w3c.github.io/webappsec-suborigins/

[11] http://www.chromium.org/developers/design-documents/per-page-suborigins

[12]
https://django-oauth-toolkit.readthedocs.io/en/latest/tutorial/tutorial_01.html

[13] https://www.npmjs.com/package/express-fileupload

[14] https://codex.wordpress.org/Administration_Screens

[15]
https://scotch.io/tutorials/build-a-restful-json-api-with-rails-5-part-one

[16] http://cdn.ckeditor.com/4.4.8/full-all/samples/replacebyclass.html

[17] https://support.google.com/richmedia/answer/117857?hl=en

[18] Data from surveys of vulnerabilities at Google indicates that legacy
code
is disproportionately likely to contain XSS bugs. One prominent example was
a
decade-old “Google Zeitgeist” static page hosted under a sensitive domain,
which suffered from a DOM XSS bug which affected a large number of other
applications.

[19] https://bugs.chromium.org/p/chromium/issues/detail?id=651822

[20] https://w3c.github.io/webappsec-suborigins/#unsafe-cookies

[21] https://w3c.github.io/webappsec-suborigins/#intro

Received on Friday, 3 November 2017 23:40:59 UTC