I recently had the opportunity to embed Rhino into a third-party web application. The purpose was to permit users to upload (untrusted) scripts. Those scripts could then run with the privilege of the user in question, enforced by the security model inherent in the webapp's API. It all seemed too easy to be true, until I uploaded a script that began with:
var CLASS = embeddedObject.getClass().getClass(); var FILE = CLASS.forName("java.io.File");
...and it went downhill from there rapidly.
I want to expose some of the web application's operations to the
One possibility is to explicitly implement
on classes that wrap the application's objects.
Unfortunately, the object graph reachable from one of the embedded
objects covers members of quite a few classes; and I wanted to avoid
having to wrap all of those classes in Scriptable implementations by hand.
One possibility would be to generate those classes semi-automatically,
either at runtime or compile-time. In fact, Rhino already implements the
runtime wrapping itself, which led me to the solution below.
Now, those web application objects require certain levels of security
privilege (reading files, writing files, etc) for particular operations.
There's no getting around this; that's just the way the webapp was
written. The privileged operations live in a few localised places,
but nevertheless, making large and invasive changes to the web app (someone
else wrote it) wasn't really feasible. For instance, sprinkling
calls throughout it would be problematic. And it wouldn't actually
be the end of the problem.
Additionally, because the webapp runs under a traditional security
manager (tomcat's), I was limited in what I could do without
since if I were to slap a restrictive policy on Rhino then the embedded
(This is the whole raison d'etre of
The last part of the problem is this: even without a restrictive
security policy in place, the embedded objects colaborate to enforce the
programmatic security model of the web application. That's a good thing;
those privileged classes I mentioned rely on this to permit or
deny those pesky file operations.
however, effectively uses objects as capabilities (if you can reach the
object you can perform the operation); thus, unscrupulous code could
create new capability objects (by simply calling
effectively circumventing the cooperative security model in the webapp's API.
All too many classes had public constructors; again, I was looking at
extensive (and possibly unworkable) changes.
Unfortunately, rhino exposes the
getClass() method on
those objects when
it wraps them, and from there it's a relatively small step to being able
that turns off the runtime permission getClassLoader,
accessDeclaredMembers and accessClassInPackage.* unfortunately
cripples Rhino, because it uses those permissions internally in order
to provide its object-wrapping.
seemed to be the major stumbling block. From there,
the reflective world is your oyster.
So I made a couple of small changes: firstly, removing
getClass and the other
objects installed by
secondly, by a gross hack in
if ("getClass".equals(name)) continue;
at the appropriate spot. The resulting (rather neutered) Rhino appears
to still permit me to expose to untrusted scripts the functionality I'm
after; and I _think_ it's relatively safe. I added a completely restrictive
ClassShutter implementation to be on the safe side.
If your application has a nice component model, chock-full of factory methods (where appropriate) and you can get at all the functionality you need by following the object graph from a few key starting points, then you're onto a winner with this.
On the other hand, if your API requires extensive use of
in the client code in order to operate, then (a) shame on you! and (b)
you're out of luck. Fortunately, the webapp this story all sprang from
fell into the former category.
It's worthwhile adding that the interception of object creation and method calls was really what I was after here. Doing this within Java's extant security framework would have required a great deal of effort (and might actually have proved impossible given some of the other constraints). What I really could have done with, of course, was a way to instrument code (possibly at runtime), including (perhaps) the core Java classes, to permit me to let Rhino call the bits of functionality I was interested in exposing - and no others. And that's what aspect-oriented programming is all about. In fact, I've occasionally thought (whenever I think about AOP, which isn't often) that the security scenarios are far more interesting and compelling than the other old AOP chestnut, logging. The danger is always that security considerations (which are one of those cross-cutting concerns) are complex and the separation of the security can lead to it rotting, just as much as it can mean that it's all in one place for ready inspection. Swings and roundabouts, I think. But I'll probably revisit this with an AOP solution if I can be bothered to sort one out. (My next goal is to do something similar to what I've outlined here for SnipSnap, the object model of which is not so clean-cut, so I may be falling back on AOP sooner than I'd otherwise anticipated.)
It's an invasive approach, but something else has just occurred to me: the Rhino wrapper-generation classes could readily be modified to look for Java 5.0 decorations on classes and methods. Again, that would permit the controlled exposure of _only_ the desired methods. The problem with this is that it means you can't have a method return a java.util.Anything because you don't get to attach metadata to classes you don't write yourself.