Widget Security

I've had a look at some real-life Widgets during the last two or
three days and, as a result of that, would have some input to the
security considerations.  Or rather, I'd like to suggest a section
(or a note) on good practices for Widget (and, in fact, Web
application) development.

(Note that much of this also applies to Web applications that run in
a browser and reach out to multiple origins to obtain data.  Based
on what I've seen in the last couple days, I've come to believe that
we're in for some interestingness in that area, too.)

Actual widgets look pretty bad in terms of code quality.  The
Twitter widget issues I described last are really just the tip of a
rather large iceberg.  There are more vulnerable widgets, and more
severe vulnerabilities.

However, in this message, I don't want to focus on individual
examples, but rather on what this group should do.

Here are some of the things to discuss in terms of development good
development practices; I'm sure this can be organized much better:

- The traditional Web programming model in which (a) the sandbox
  keeps any security issues in check, and (b) your server-side Web
  application needs to validate the data anyway (and you can
  therefore be sloppy on the client) doesn't hold any more.  The
  JavaScript code is the trusted Web application now, and the server
  is untrusted.

- Widgets may or may not have access to local capabilities.  There
  might be some way for them to ask for limited capabilities.  If
  that is the case, Widges should be written in a way that demands
  only the capabilities absolutely necessary (least privilege). They
  should avoid using (or getting access to) facilities such as the
  MacOS widget.system API, which effectively gives the widget full
  user access to the computer.

- When widgets run with privileges that extend beyond the browser
  sandbox, and if they reach out to the network, then they are
  effectively custom-built network clients.  They must treat any
  data received from the network as untrusted information, and they
  must be paranoid about what they do with that data.

  One particular example is that you shouldn't just run eval on JSON
  data that you get from "home" (or from another source); you
  shouldn't insert script tags into the widget's DOM to request
  third-party JSON data (use XHR and a custom parser instead).

  Also, if you want to display Web pages, don't download them and
  render them by dropping them into the Widget's own DOM.  Make sure
  they run in a separate DOM.  (E.g., by using openURL.)

- Widgets are susceptible to a client-side equivalent of
  cross-site-scripting attacks: If data retrieved from the network
  is written to the widget DOM in a way that can cause the
  uncrontolled creation of elements, then an attacker can once again
  take over the widget.
  
  Techniques such as writing to the document using the
  Document.write() method or the (not-standard) innerHTML property
  are particularly risky.  These should not be used; instead, text
  nodes can be created more safely using, e.g., the
  Document.createTextNode() method.

  Code insertion attacks are also possible when creating attributes;
  it is good practice to *not* use data retrieved from the user or
  (more importantly) the network when, e.g., constructing event
  handlers for an attribute that's dynamically written.

  Note that, if widgets run with privileges beyond the traditional
  browser sandbox, the results of this attack vector be severe
  enough to be a convenient vector for causing a system compromise.

The last point is incredibly important (and *very* easy to get wrong
when programming in a certain style); I'm currently waiting for a
major vendor to fix a bug like this in one of their widgets, and
will then have a juicy example to talk about.  I frankly anticipate
this to be a source of attacks against widgets for some years to
come.



In fact, there might be a way to mitigate the "cross-site scripting"
side of things that could be attractive to spec and deploy: What if
vendors were to put a bargain into code in which the use of
innerHTML, Document.write, and friends, or the insertion of script
tags which load a script from an external source would tie the
Widget to the traditional browser sandbox model?

There are at least two ways to do this:

- Restrict future JavaScript method invocations to the traditional
  sandbox model once the risky coding style has occured (i.e.,
  innerHTML, document.write, ...).  This is most likely to have the
  least impact on existing widgets; however, from a programmer's
  perspective, it is likely to lead to a certain amount of pain, and
  to hard-to-debug bugs.

- Throw a security exception if any of the risky techniques are used
  in a Widget that has access rights beyond the traditional sandbox.
  That would be feasible for some of the currently deployed engines,
  but certainly not for all of them.
  
  (E.g., the dashboard widget engine has somewhat finegrained
  control, whereas the Windows Vista sidebar engine doesn't seem
  to.)

I'd like to see discussion of this some time soon, and will follow
up with some examples -- the risky techniques are really all over
the place.  Having anonymized descriptions of some example flaws and
the attacks that work against them would probably be a useful thing
for such a security considerations section.

Incidentally, most of these considerations also apply to Web
applications that mash up data from *different* origins; the
same-origin policy indeed has the side effect to localize the
consequences of bad client-side scripting.  Once again, I wonder if
it would be worthwhile to reduce the attack surface by creating a
need for a client-side declaration that a script intends to go
beyond the traditional sandbox, and (if that is done) trapping of
document.write and friends with security exceptions.

(I'd love to trap eval() the same way, in fact, but that's far
beyond just manipulating the DOM.  Still, might be worth thinking
about.)

Cheers,
-- 
Thomas Roessler, W3C  <tlr@w3.org>

Received on Wednesday, 5 December 2007 08:44:42 UTC