Java 2 Platform Security Exposed
By Dean Wette, OCI Senior Software Engineer
February 2001
Introduction to Java Security
Security is perhaps one the most visible, yet least-understood, aspects of Java.
Starting with the first version, every logical and physical component of the Java platform has been designed and implemented with security as a chief concern. This includes:
- The Java language itself, which prevents dangerous operations, such as array overruns and direct memory access via pointers
- A compiler that checks for specification-conformant source code
- A runtime environment that ensures that compiled code is well-behaved and safe to execute according to rules of the Java platform.
Java 2 includes many significant changes and improvements over its previous versions.
The security model represents a significant evolution in Java, both in terms of architecture and API support in the core libraries and optional packages. Surprisingly, the new security model in Java 2 is also one of the least discussed enhancements.
Indeed, while the press has been very prolific with new literature on the Java 2 platform, only a handful of new books cover Java security in enough detail to be meaningful, and books devoted to Java 2 security aren't anywhere as numerous as those on other Java topics.
The Java 2 Security Model
Generally speaking, Java security is class-based, where trust is given to executable code (bytecodes found in class files), according to both:
- Where the code is located
- Whether, and by whom, the code is digitally signed
A mapping of code to signers is represented by the java.security.CodeSource
class, which encapsulates a URL pointing to the code's location and a list of zero or more digital certificates used to verify signed code originating from that location.
In versions prior to Java 2, trust is all-or-nothing; that is, either code has complete trust to execute freely and perform any operation supported by the Java platform, or the code is untrusted and runs in a 'sandbox' where it is restricted from performing operations that access sensitive local and network resources.
In all versions of Java, Applets and other code loaded from the network run in this sandbox and are prevented from accessing file systems, devices, sockets, and information about the environment and user. In effect, they can only utilize local memory, the CPU, and the web server from which they are loaded.
On the other hand, Java applications and other code loaded from the local file system are trusted unconditionally to perform sensitive operations. Java 1.1 features packaging of class files into Java Archive (jar) files that can be digitally signed, allowing Applets to run trusted with full resource access. However, this course-grained approach to security is too limiting or too free for many situations.
Java 2 alleviates the 'all-or-nothing' scenario with a new security model and supporting APIs that provide a fine-grained, policy-based access-control mechanism with the following characteristics:
- A permission-based security policy that is easily configurable
- An enforcement mechanism that is separated from the security policy statement
- An access-control structure that can be extended to satisfy unique security requirements
- Expansion of security checks to all Java programs
In effect, trust and access control can be fine-tuned for all Java programs using policy files configured independently of security implementation and enforcement.
By default, Java Applets and applications run as they always have in terms of trust and security, but now options exist for specifying security policies that selectively relax Applet sandbox restrictions or subject local code to more restrictive access control.
Policy Configuration
Sun's standard distribution of the Java 2 platform utilizes text files for policy configuration, with a system-wide policy file deployed automatically with the Java Runtime Environment (JRE) and optional user policy files located in user home directories. Other policy configurations are possible with the use of additional policy files, or with alternate methods of policy-statement persistence (e.g., database or network-based policy storage).
Policy configuration consists of an optional 'keystore' entry that specifies the location and type of digital certificates used to sign code, plus zero or more grant entries specifying permissions given to code sources (i.e., code URL plus digital signers).
A grant entry might look something like this:
grant signedBy "honestjoe", codeBase "http://joe.org/util.jar" {
permission java.io.FilePermission "/usr/home/dwette/*", "read,write";
};
This entry grants the code in util.jar
, found at the joe.org web site, permission to read and write files in dwette's home directory, if and only if the code is also signed by the signer identified by the alias honestjoe. Omitting the signedBy
clause grants permission to unsigned code as well.
When the Java runtime loads the code specified in the grant entry, it associates loaded classes with protection domains that map the code source to granted permissions. If code is also loaded from some other location, it becomes associated with a different protection domain, with its own permission mappings, making it impossible to spoof the policy or fool enforcement of access control.
The runtime representation of protection domain mapping utilizes CodeSource
objects associated with collections of immutable objects derived from the java.security.Permission
class (such as the FilePermission
given in the example).
These permission objects are used at runtime for the actual access control checks. The core Java 2 APIs include eleven Permission classes representing access permissions to resources such as file systems, network sockets, environment, and user information. These built-in permissions suffice for many applications, but facilities exist for creating new Permission classes unique to application requirements, which are in turn supported automatically in policy grant statements.
The Security Manager and Access Controller
Permission checks are only performed if a security manager is installed. Applets always run with a security manager, installed automatically by the host browser. Applications, however, only execute with a security manager if one is specified explicitly, either in the application code or through an option provided to the java command.
The security manager class (java.security.SecurityManager) represents the runtime focal point of access control in Java 2; it is called by operations whenever a decision needs to be made for granting or denying resource access. It does this by determining the type of permission needed and delegating the actual check to the access controller (java.security.AccessController).
The access controller determines if permissions granted to the code requesting access imply the Permission needed to grant the specific request for access. If the access controller denies access, it throws a runtime security exception; otherwise, it returns silently.
If no security manager is present, access checks are not carried out at all.
The Access Controller and Privileged Blocks
The access controller makes security determinations using the 'principle of least privilege,' which means a request for access to a protected resource is granted if and only if every protection domain in the current execution context (call stack) has access permissions to the requested resource.
In other words, every method call in the current execution stack – starting with the current caller and moving through all ancestral callers – is examined to make sure the calling code has been granted permissions necessary for requested access. If, at any point, code is encountered that lacks the permission, a security exception is raised without further ado.
This implies that untrusted code cannot use trusted code to gain permission for access it doesn't have otherwise. However, it also means that trusted code cannot perform secured operations on behalf of other code, which can be too restrictive under certain circumstances. In fact, the core Java APIs need to perform various operations regardless of calling context.
To solve this problem, a block of code may be marked 'privileged,' in the sense that it assumes responsibility for exercising its own permissions and informs the access controller to ignore previous callers.
However, privileged code cannot gain access permissions as a result, and the principle of least privilege still applies, but only from the point of the caller requesting access to that of the privileged block. Callers to the privileged block are ignored.
A code block is marked privileged by wrapping it in a java.security.PrivilegedActionobject
that is passed to the AccessController
's doPrivileged()
method.
For security reasons the privileged action objects passed to doPrivileged()
are usually implemented with anonymous inner classes.
Privileged blocks stand as one of the most interesting facets of the Java 2 security model. Further details and examples can be found by consulting the standard Java API documentation.
Some Other Features of Java 2 Security (In A Nutshell)
Class loaders, which locate and fetch class files for linking into the Java runtime, form an important relationship with the Java security model. Their responsibilities include all of the following:
- Establishing protection domains for loaded classes
- Ensuring that newly loaded classes don't preempt more trusted classes (such as the core API classes in the java.* packages)
- Ensuring that bytecodes found in class files are safely verified before being allowed to run
The security enhancements in Java 2 include significant improvements to the class-loading mechanism and supporting classes.
Support for digital signature and cryptographic services comprises another important component of security in Java 2.
The Java Cryptography Architecture (JCA) design features provider-based, algorithm-independent cryptographic services that allow multiple interoperable implementations and associated standards to be used without requiring modification to existing APIs. Thus, as improved cryptographic services become available, they can easily be plugged-in to existing applications.
Some of the current implementations are strong enough to warrant U.S. export controls (these controls have been relaxed recently). For that reason the export-restricted parts, along with some supporting APIs, are available separately as an optional package, the Java Cryptography Extension (JCE).
The JCA also includes the java.security.SignedObject
and javax.crypto.SealedObject
classes, which provide facilities to ensure validity and confidentiality of serializable Java objects. This feature allows for the protection of runtime information outside of the Java environment or during transport between Java Virtual Machines.
The Java 2 platform also includes several supporting security tools:
policytool
. GUI tool used for configuring policy fileskeytool
. For creating and manipulating digital signatures and certificates, and for other cryptographic-services configurationjar
andjartool
. For creating, signing, and verifying Java Archive (jar) files
Sun offers additional optional packages for implementing security in the enterprise with the Java Secure Sockets Extension (JSSE), for building secure network applications, and the Java Authentication and Authorization Services (JAAS), for adding user-centric access control to applications.
Moving Forward
This technology brief barely scratches the surface of Java 2 Security. The core APIs provide much more than represented here, and security support for enterprise Java systems is mentioned here only briefly.
While the Java security model is simple to use once its fundamental concepts are understood, it is also sophisticated and powerful.
Sun is not resting on this accomplishment, however. In the future more improvements and enhancements can be expected, not only in the core APIs, but with enterprise Java technologies as well. Already on the horizon are new security support for mobile devices, improved security mechanisms in the Java RMI (Remote Method Invocation) APIs, and interoperability with OS based security services.
Foreseeable future enhancements and improvements might include dynamic permission revocation, improved and more flexible access-control algorithms with fine-grained tuning of privileged block permissions, XML-based policy configuration, and XML encryption and digital signatures.
References
Note: Li Gong is the Chief Java Security Architect at Sun Microsystems, Inc.
- [1] Li Gong. Inside Java 2 Platform Security. Addison-Wesley, 1999.
- [2] Jamie Jaworski and Paul J Perrone. Java Security Handbook. SAMS, 2000.
- [3] Jonathan Knudsen. Java Cryptography. O'Reilly, 1998.
- [4] Scott Oaks. Java Security. O'Reilly, 1998.
- [5] Marco Pistoia, et al. Java 2 Network Security. Prentice Hall, 1999.
Software Engineering Tech Trends (SETT) is a regular publication featuring emerging trends in software engineering.