First: this isn't completely, or even mostly, my own idea. It's a bit
of things I've heard or seen over time, combined with my own thinking,
hopefully assembled into an entire and consistent package. I'm going to
provide links to credit where the ideas/facts come from whenever possible.
Fair warning: this is pretty long, and follows a few tangents. So be
prepared to read for a while. Jump to the "Solution on the horizon"
section if you're busy/impatient.
== Why a Change? Change to What? ==
Some quick history. Greasemonkey started out, way back in late 2004 [1]
as a very simple almost proof-of-concept. There was a full XPI [0] or
two, designed to fix/enhance particular sites, and Aaron realized a
general platform for quickly making such customizations was a good idea.
Greasemonkey was born. It was in March 2005 when the can of worms was
opened, GM_xmlhttpRequest was added [2].
I don't think there's anyone who objects to having cross-domain XHR for
user scripts. It's enabled quite a few amazing things, like the early
and (at least at the time) rather famous Book Burro [3]. But, for
better or worse, GM_xhr has been the crux of a variety of
security-related issues. The first happened in July of 2005 [4]. Back
then, Greasemonkey injected both the scripts, and the privileged
functions like GM_xhr directly into the content window.
== Security, and Where We Are Today ==
Rather quickly, at the end of July 2005 [5], the first (beta) release of
Greasemonkey 0.5 was released. This is where Greasemonkey entered the
world of XPCNativeWrappers and (Components.utils.)Sandbox. To keep it
short: they're designed to solve just about exactly the problem that
Greasemonkey was facing. And they're effective.
Perhaps too effective. Besides one more hole from early 2008 [6],
Greasemonkey has been quite secure, but at the cost of various kinds of
confusion and degradation. At the very least:
* There's a long and detailed article about how to deal with the
not-quite-normal environment that Greasemonkey scripts run in [7] ...
* ... but the average user doesn't know about it and will still today
ask questions like "why does this work when I try it in a page but not
in Greasemonkey?" [8] (And not even in the right place!)
* Integration with Firebug's present, but flaky. [9] [10]
* Error reporting is intermittent and/or confusing. (I lack links, but
the line number is often wrong, errors in scripts often point to
components/greasemonkey.js and lately they go to the Error Console, but
not the Firebug console very often).
I could go on. For an expert like myself, these problems all add up to
be very minor. For beginners, however, they can be very mysterious and
difficult to understand. And Greasemonkey is supposed to be *easy*!
Its original spirit involved quickly creating one short file (just a
handful of lines long) and getting immediate results. I want that
spirit to thrive.
== A Solution on the Horizon? ==
JavaScript is an unusual, perhaps even quirky language. Personally, I
love its flexibility and power. But with great power ... I'll hold back
the cheesy quote. (Please don't obsess over this statement. Note that
javascript the language, and javascript as it is implemented into web
browsers are two separate topics. Like Greasemonkey, JS in the browser
is a happy accident of history, where useful things became popular, and
thus de-facto standards. It's a common occurrence in the real world.)
It is unusual in that it lacks language support for (in other languages)
common features like class-based inheritance, private members, and so
on. Since it is powerful and flexible however, these features can often
be built out of JS itself. Most JS frameworks provide a class-like
inheritance system. And though they don't look like a class-based
private, JS can enforce access to variables via closures.
Closures as a security barrier have been suggested before [11]. We've
even had discussions here on gm-dev about secret tokens, in "private"
variables via closure [12] before.
The magic juice would be something that I've done before (for work),
which is two simple steps: first register globally-available javascript
methods for all content [13], via well defined XPCOM facilities. Then
require a secret token before allowing those methods to be called. The
token should be a salted hash of the script ID, where the salt is a
random value (generated at GM install time), different for every user.
* At install (or modification) time, each script would be 'rolled up'
into a single file containing the script, any @requires, and any
wrappers needed to isolate it.
* In those wrappers would go helpers for all the GM_ APIs. A closure
would grant these functions (and the rest of the script) access to a
token that that has been injected as the script was assembled.
* The helpers take the token which they have access to, and pass it
along to the real (XPCOM exposed) API, which verifies the validity of
the token before taking any action.
* This generated script is inserted into the page itself as a <script
src=...>. Since it is third party, the page has no way to know anything
about the contents of the script, seeing only the value of the src
attribute. (This could even be randomized/anonymized to limit exposure.)
Phew! So if we do all this, what happens?
* The script runs _in the page_, so everything any existing JS author
expects to work, will work.
* Access to the Firebug console and/or debugger should work just fine:
the same way any dynamically-added script would. Being a regular
<script> at a consistent URL should make it easier to dig into via debugger.
* Error reporting will work, consistently. (We'll probably still want a
wrapper, because each script is potentially made up of itself plus a
bunch of @requires, we'd want to point to the right place.)
Or, ideally, it would "just work". Like Johan mentioned in my 0.9
thread, all the features that modern Firefoxes, HTML5, and so on are
providing are making (some of) the GM_ APIs less and less relevant.
Along this path, we might consider deprecating some of them.
== Potential Problems? ==
I would want to remove the apiLeakCheck along this path, because of bugs
it directly creates. Unfortunately, this hands a giant footgun to
script authors: they could do something like "window.foo =
GM_xmlhttpRequest" and completely violate the private-member nature of
this security barrier. This might be "okay" because it's not likely (I
can't imagine, under this scheme, a reason a script author might want to
do this). Or, we might be able to refactor the leak check to ensure
that some user script is on the stack somewhere (it is a script with a
src, so should show up separately on the stack .. I think).
== Wrapup ==
I'm a little excited about this whole idea. I've thought through it in
the back of my mind for a while. Then once it was really formed,
thought through it with the front for a while too. It seems pretty
solid. Building a proof-of-concept would be pretty important. But
that's likely a long way off, for now.
[0]
http://github.com/greasemonkey/greasemonkey/blob/e7ecbd3222c8880af198aa65b49ff79448adeac0/www/index.html
[1]
http://github.com/greasemonkey/greasemonkey/commit/f950196e4146cf2f4ba993a3a35c38c068ed0254
[2]
http://greaseblog.blogspot.com/2005/03/greasemonkey-025-xmlhttp-across.html
[3]
http://overstimulate.com/articles/greasemonkey-book-burro-find-cheap-books
[4] http://mozdev.org/pipermail/greasemonkey/2005-July/004022.html
[5] http://greaseblog.blogspot.com/2005/07/greasemonkey-05-beta.html
[6] http://groups.google.com/group/greasemonkey-dev/t/933ecdb307c4386d
[7]
http://commons.oreilly.com/wiki/index.php/Greasemonkey_Hacks/Getting_Started
[8] https://bugzilla.mozilla.org/show_bug.cgi?id=525560#c14
[9] http://github.com/greasemonkey/greasemonkey/issues/#issue/1024
[10] http://github.com/greasemonkey/greasemonkey/issues/closed/#issue/1018
[11] http://groups.google.com/group/greasemonkey-dev/t/3b93d85c049fc9c0
[12] http://groups.google.com/group/greasemonkey-dev/t/ae8af5f66724423f
[13] http://weblogs.mozillazine.org/weirdal/archives/017211.html