Some of you may have noticed my recent patch [1] to the AS which factors
out a couple hundred privileged blocks into 20 or so common classes. I
plan to do more such factoring, but I want to talk a little bit about
what privileged actions are all about first. This isn't just an
introductory text - I also want to get the more experienced folks
thinking about privilege in a new way. For the subject matter review,
read my previous email "On security contexts and propagation".
There are essentially three different contexts we need to consider here:
1) Privileged context - the method is definitely only ever invoked from
other known-to-be-privileged context
2) Maybe privileged context - the method in question might be called
from user code, or it might be called from privileged code
3) Unprivileged context - the method in question is intended to be
invoked from user deployment code and there is a high degree of
certainty that the caller is unprivileged
Those of us who are already security-aware often have the automatic
reaction of putting absolutely everything that requires privileges into
doPrivileged constructs. But, this is a practice we should definitely
reexamine. Calling through doPrivileged is not free, and the GC
pressure resulting from all the temporary objects have a real cost as
well. In addition, the anonymous classes which often accompany these
actions cause class loading time to increase and permanent generation to
be consumed for actions which are often duplicated in many places.
If you're writing a method that is known to run in a privileged context
already, you do not in fact need any doPrivileged at all. This should
include methods in DUPs, extensions, management operations, thread
pools, and (most) interceptors.
Most framework API methods fall under the categories of #2 or #3.
Methods of this type should be subjected to the following question:
Does this method allow the caller to perform one or more actions which
they would not otherwise be authorized for?
If the answer is "yes", then you usually need two things: a privileged
block to execute the action (of course), and a permission check to make
sure the caller has permission to take the given action.
It may seem like the second is redundant: what is the purpose of taking
away one permission check and adding another? It is often the case that
an operation is very specific in purpose, but requires several
permissions in order to run. It also may be the case that granting the
unprivileged caller the full set of required permissions will give them
an undesirable level of access; in these cases it is wise to grant the
caller a narrow permission with a single purpose, which is checked for
in the privileged context.
As a final consideration, there are often cases where the privileged
action is encapsulated by an already existing object. In some cases,
you can simply have your object directly implement PrivilegedAction or
PrivilegedExceptionAction, thus allowing doPrivileged to call directly
into your object without having to instantiate an anonymous class. See
[2] for an example of this - by having InterceptorContext implement
PrivilegedExceptionAction we're able to reuse an already-existing object.
Going forward I would like to see better documentation - especially on
API methods - about what the security requirements and effects are for a
given method. More on this in the future.
[1]
https://github.com/jbossas/jboss-as/commit/8ca603eb9d
[2]
https://github.com/dmlloyd/jboss-invocation/commit/b654b48cbb79
--
- DML