code, to explicitly declare their intentions when performing sensitive operations. This is akin to the setuid feature
in Unix except that, instead of enabling
system-high privilege for the entire
program, in Java the privileged mode
lasts only as long as the duration of the
privileged method call. Note that if this
privileged code later calls less privileged
code, the effective set of privileges will
still be curtailed because of the principle of least privilege.
The need for explicit declaration may
appear cumbersome at first, but programmers can rest assured that their
code will not unintentionally weaken
security. Furthermore, the majority of
programs do not need to invoke their
privileges specifically, so we have given
the most common programming cases
the best of both worlds—ease of coding
and peace of mind. Note that it may not
be easy for programmers to figure out
exactly which of their privileges they
need to declare in order to make their
programs work properly in all possible
scenarios. The JDK 1. 2 design actually
does not support fine-grained privilege
declarations. A declaration enables all
the privileges associated with the code.
The major lesson here is that being
systematic is easier and more robust
than being ad hoc, though not everyone
understands this. Toward the end of
JDK 1. 2 development, during a security
audit of the entire code base (which got
enacted after much begging, plus sticks
and carrots), we discovered that a Sun
engineer working on JDK had deliberately duplicated and then modified system code such that his own library code
would not have to bother with an explicit declaration of privileges—a move
that may have made his job slightly
easier but would have led to serious security breaches for all users of the Java
platform if his misdeed had gone undetected.
A number of hard problems remain
in this area. Top among them is whether
least privilege calculations can be done
efficiently, especially with very complex security parameters—for example,
complicated access-control policies,
many different types of access rights,
and an intricate execution environment. Another problem is that assigning different privileges to different code
created complexities for other parts of
the system. For example, optimizations
a piece of code
can explicitly
declare to exercise
its own privilege
unilaterally,
telling the security
system to ignore
codes that have
come before it.
this design lets
programmers to
explicitly declare
their intentions
when performing
sensitive operations.
done by JIT (just-in-time) compilers
must now conform to additional security requirements.
Yet another perennial problem is
the practical side of security policy
management and deployment. A more
theoretical question, but one nonetheless worth pondering, is the scope of
security policies that can (or cannot)
be enforced with the least-privileged
model, using the rather conventional
categories of access-control permission types defined in JDK 1. 2. Fred Schneider of Cornell University developed
an intriguing concept called Inline Reference Monitor and proved that it can
express and enforce any and all policies enforceable by monitoring system
execution.
5
Despite these difficult issues, one
comforting thought may be that, after
more than 12 years in the field, the principle of least privilege as architected in
JDK 1. 2 has stood the test of time and
probably saved untold numbers of coding mistakes from turning into security
blunders.
the importance of Being earnest
Many other technical lessons are worth
repeating periodically. For example,
you should be very judicious about
the use of NULL, because you cannot
change the behavior of nothing. In JDK
1.0/1.1, in certain circumstances, the
ClassLoader or SecurityManager
could be NULL, which made it difficult
to retrofit a more fine-grained design.
As another example, during runtime
Java turns static code into live objects.
This process actually contains two
separate steps: locate the code description; define it into a live object. The first
step should be open and extensible in
nature, because both the runtime system and applications should be able
to specify desired locations for obtaining code. The second step, on the other
hand, must be strictly controlled so
that only trusted system code can handle the job of creating objects. Unfortunately, these two steps were overloaded
into a single method, which worked
well in the all-or-nothing model but
caused much difficulty when JDK 1. 2
changed into a more nuanced world.
Another issue may be surprising to
many people: strictly speaking, Java
cannot guarantee sequential execution of consecutive instructions. One