The parameters of the Java sandbox that we’ve outlined are possible elements of a Java application, but they are not required elements of an application. The remainder of this book will show us how and when those elements can be introduced into a Java application. First, however, we’re going to discuss the techniques by which Java applications can be run.
There are two techniques that we’ll introduce in this section:
the JavaRunner
technique and the
Launcher
technique. While both allow you to run
an application securely, the examples in this chapter do not provide
any security. We’ll fill in the security pieces bit by bit,
while we flesh out the security story. At that point, we’ll
show how to run Java applications securely.[2]
Typically, we’re used to running Java applications simply by
specifying on the command line the name of a class that contains a
main()
method. Consider this application that
reads the file specified by a command-line argument:
public class Cat { public static void main(String args[]) { try { String s; FileReader fr = new FileReader(args[0]); BufferedReader br = new BufferedReader(fr); while ((s = br.readLine()) != null) System.out.println(s); } catch (Exception e) { System.out.println(e); } } }
This is a regular Java application; if we wanted to run it and print out the contents of the password file on a Unix system, we could run the command:
piccolo% java Cat /etc/passwd
root:x:0:1:0000-Admin(0000):/:/usr/bin/csh
daemon:x:1:1:0000-Admin(0000):/:
bin:x:2:2:0000-Admin(0000):/usr/bin:
...
From a security point of view, this is a very rudimentary program. It contains none of the elements of the sandbox that we just listed; it has the default (wide-open) sandbox given by default to every Java application. This application can perform any operation it wants.
There are two ways in which we can add security features to this application. One way is to add to the application a class loader, a security manager, use of the access controller, and so on. This additional programming would set the bounds of the sandbox for this particular application.
The other route we can take is
to run this application under the auspices of another application
that we’ll call JavaRunner
. This is
completely analogous to the way in which we typically run applets:
appletviewer
is a Java application that runs
applets, and JavaRunner
is a Java application
that runs other applications. Java-Runner
is
responsible for establishing the parameters of the Java sandbox (that
is, it ensures that appropriate class loaders, a security manager,
and the like are all in place) before it invokes the target
application, just as appletviewer
establishes
the parameters of the Java sandbox before it invokes the target
applet.
This technique removes the difference (in terms of security) between an applet and an application: both types of programs are now subject to the Java sandbox. There are a number of circumstances in which this is useful:
If you download (or purchase) Java applications and want them to run in a sandbox.
If you want to ensure that your internally developed applications all run in the desired sandbox (without having to include that code in every application).
If you have a corporate or campus network and need to distribute Java applications under a new security model. Perhaps the new model will:
Give different security permissions to programs downloaded from within the corporate firewall than those from outside the corporate firewall (without requiring internal classes to be signed)
Authenticate users on the corporate network before allowing sensitive payroll data to be sent (even over the corporate network)
Encrypt that payroll data, so internal spies can’t decipher it
Allow the user greater discretion over the resources granted to a particular program
Although the JavaRunner
program is designed to
run other applications, there is no reason why it cannot be modified
to run applets as well. Such a modification would require some extra
code to parse the HTML containing the applet tag and set up an
instance of the AppletStub
and
AppletContext
classes for the applet itself.
We’re not showing the code to do that only because it’s
not really relevant to the discussion of Java security—but the
JavaRunner
could easily be extended to become an
appletviewer
(or, with an appropriate Java bean
that interprets HTML, a full-fledged browser). The advantage, of
course, is that as author of the browser you would have full control
over the security model the browser
employs.
Here’s the basic implementation of the
JavaRunner
application:
public class JavaRunner implements Runnable { final static int numArgs = 1; private Object args[]; private String className; JavaRunner(String className, Object args[]) { this.className = className; this.args = args; } void invokeMain(Class clazz) { Class argList[] = new Class[] { String[].class }; Method mainMethod = null; try { mainMethod = clazz.getMethod("main", argList); } catch (NoSuchMethodException nsme) { System.out.println("No main method in " + clazz.getName()); System.exit(-1); } try { mainMethod.invoke(null, args); } catch (Exception e) { Throwable t; if (e instanceof InvocationTargetException) t = ((InvocationTargetException) e) .getTargetException(); else t = e; System.out.println("Procedure exited with exception " + t); t.printStackTrace(); } } public void run() { Class target = null; try { target = Class.forName(className); invokeMain(target); } catch (ClassNotFoundException cnfe) { System.out.println("Can't load " + className); } } static Object[] getArgs(String args[]) { String passArgs[] = new String[args.length - numArgs]; for (int i = numArgs; i < args.length; i++) passArgs[i - numArgs] = args[i]; Object wrapArgs[] = new Object[1]; wrapArgs[0] = passArgs; return wrapArgs; } public static void main(String args[]) { if (args.length < 1) { System.err.println("usage: JavaRunner classfile"); System.exit(-1); } ThreadGroup tg = new ThreadGroup("JavaRunner Threadgroup"); Thread t = new Thread(tg, new JavaRunner(args[0], getArgs(args))); t.start(); try { t.join(); } catch (InterruptedException ie) { System.out.println("Thread was interrupted"); } } }
This is a fully functional (if not full-featured) version of the
JavaRunner
program; we can use it to run our
Cat
application like this:
piccolo% java JavaRunner Cat /etc/passwd
root:x:0:1:0000-Admin(0000):/:/usr/bin/csh
daemon:x:1:1:0000-Admin(0000):/:
bin:x:2:2:0000-Admin(0000):/usr/bin:
...
This will give us exactly the same results as when we ran the program
by hand. TheinvokeMain()
method will use the Java
reflection API to find the static main()
method
of the Cat
class and then construct an
appropriate argument list to pass to that method. Note that the use
of the reflection API introduces a dependency on Java 1.1 for this
program. You can write a similar program under Java 1.0, but not
without using the native (C) interface to Java.
Note also that we construct a new thread group and thread, and run
the main()
method under control of that thread.
The primary reason we do that will become clear in Chapter 6 when we discuss thread security policies. But
there’s no reason why you couldn’t expand this example to
run multiple targets simultaneously, in which case each target should
have its own thread and thread group anyway.
We’ve cheated a little bit here by using the
forName()
method of the Class
class to find our target application class—we’ll hear
more about that in Chapter 3 when we discuss class
loaders. For now, it will suffice to know that this will load our
target class (assuming that the target class is found on the
CLASSPATH
). In addition, we still haven’t
done anything to set up a security manager or to enable the access
controller. As a result, the sandbox for an application run under
this program is non-existent: the bytecodes will not be verified, and
there will be no restriction on any actions that the application may
perform. But this is the example that we’ll expand upon during
the rest of this book as we add security features to it.
Don’t think that the only function of a program like this is to run Java applications (or even Java applets). Consider the Java web server—it must dynamically invoke servlets for different web requests as those requests come in. An RMI server might operate similarly, perhaps even loading the code to perform its operations from a client machine. Although we stick with this example throughout the book, the need for security in server applications parallels the need for security in end-user applications .
Beginning in Java
1.2, the Java platform itself comes with a security model built into
applications it runs. This model is based upon information in the
user’s
CLASSPATH
.
Setting the CLASSPATH
is the same operation in
Java 1.1 and Java 1.2, but in Java 1.2, classes that are found on the
CLASSPATH
may optionally be subject to a
security model. This allows you to run the application code in a
user- or administrator-defined sandbox: in particular, it uses the
access controller of Java 1.2 to
provide the same security environment for the target application as a
Java-enabled browser provides for an applet.
The successful use of this facility depends upon the class loader that the built-in application runner will use, as well as depending upon the environment set up by the access controller and security manager. We’ll examine how these facilities interact with this method of running applications in the next few chapters. For now, we’ll just outline how this method operates.
As always, Java applications are run on the command line as follows:
piccolo% java Cat /etc/passwd
root:x:0:1:0000-Admin(0000):/:/usr/bin/csh
daemon:x:1:1:0000-Admin(0000):/:
bin:x:2:2:0000-Admin(0000):/usr/bin:
...
This example loads the Cat.class file from the
user’s CLASSPATH
and runs the application
with the single argument /etc/passwd
. As always,
when an application is run in this manner, the sandbox in which the
application runs is unlimited: the application can perform any
activity it wants to.
There is a very important difference between running these examples
in Java 1.1 and running them in 1.2: in 1.2, classes that are loaded
from the CLASSPATH
will be loaded by a class
loader. The addition of the class loader to the
CLASSPATH
allows us to build a sandbox for the
application. However, none of these examples actually builds a
sandbox yet. In order to build a sandbox for these examples, we must
specify the
-Djava.security.manager
flag on the command line.
This flag enables a security manager and access controller to be
installed; we’ll discuss the details of this option in Chapter 6.
The
-Djava.security.manager
flag is only available in
Java 1.2. Without it, Java applications in 1.2 behave exactly as they
do in 1.1: they have a wide-open sandbox.
For historical reasons (and because it makes describing this facility
easier), we’ll refer to the ability to run applications with an
optional argument to specify a sandbox as the
Launcher
. Given that the
Launcher
is a standard part of Java, you might
ask why we’re going to the trouble of implementing our own
JavaRunner
. One reason is simply to make our
discussion clearer: it is easiest to understand the architecture of
Java’s security policy in the context of
JavaRunner
. Other reasons have to do with
certain limitations that we’ll discover about the
Launcher
:
The
Launcher
comes only with Java 1.2 and later releases; if you’re still using 1.1, you’ll have to use theJavaRunner
program.The
Launcher
can only run classes from theCLASSPATH
—it cannot load classes from the network or from another location. However, simply because the program in question is an application does not mean we won’t want to load its classes from a server—but we’ll needJavaRunner
to do that.The security manager used by the
Launcher
does not have all the features we might desire. While most of its features are configurable through the access controller (also a feature of Java 1.2), there are certain advanced policies that we cannot configure in that way. These features can only be achieved with some programming on our part.
Hence, both the Launcher
and
JavaRunner
are useful mechanisms for running
Java applications; which one you use depends on your particular
requirements.
Get Java Security now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.