HomeDigital EditionSys-Con RadioSearch Java Cd
Advanced Java AWT Book Reviews/Excerpts Client Server Corba Editorials Embedded Java Enterprise Java IDE's Industry Watch Integration Interviews Java Applet Java & Databases Java & Web Services Java Fundamentals Java Native Interface Java Servlets Java Beans J2ME Libraries .NET Object Orientation Observations/IMHO Product Reviews Scalability & Performance Security Server Side Source Code Straight Talking Swing Threads Using Java with others Wireless XML

Java is rapidly evolving from just a useful language for developing Web-based applets to an enterprise platform for developing and deploying mission critical applications. An enterprise-class application must possess many characteristics. Comprehensive security is, inarguably, one of these characteristics.

Many existing security models burden programmers with the obligation to understand, code and enforce the security policy of an organization. The current state-of-the-art model for implementing security suggests that a developer should not directly be involved in implementing an organization's security policy. This model provides two important advantages:

  1. It reduces the likelihood of an incorrect implementation, thus increasing the security of the overall system.
  2. It allows dynamic changes to the security policy to take effect quickly. Typically, changes to an organization's security would not require an application recompile. Ideally, changes to the organization's security policy will not even require application rerun/restart.
From its inception, Java has had several built-in security features. The original JDK 1.0.x provided security through three mechanisms:
  1. The Java language code safety via the sandbox model
  2. The Java built-in bytecode verifier
  3. The Java SecurityManager and ClassLoader classes
JDK 1.1.x provided further security by introducing the concept of trusted applets. JDK 1.1 also provided classes for producing and verifying digital signatures of arbitrary data. Finally, the Java Cryptographic Extension (JCE) to JDK1.1 introduced the notion of privacy-protection of arbitrary data through encryption classes.

JDJ has published a number of articles dealing with Java security. Specifically, in an article entitled Implementing a Security Policy (JDJ. Vol. 2, Issue 8), Qusay Mahmoud wrote on practical uses of the Java SecurityManager class. Then, in an article entitled Java Security: Beyond Code Safety (JDJ Vol 2, Issue 12), I wrote about practical uses of the cryptographic interfaces of JDK 1.1, including their use in creating and verifying the source of trusted applets.

This article focuses on Protection Domains - a new JDK 1.2 security feature for implementing fine grain access control.

Trust Model before JDK 1.2
The original JDK implemented a simple and somewhat effective access control model. This model - known to Java developers as the sandbox - provides a binary choice within the runtime environment: trust everything that is local but do not trust anything that is downloaded. In other words, the Java runtime completely trusts every Java application and local applet, and it completely restricts every downloaded Java applet. However, most applets need to interact with users in a personalized way. This means that in order for an applet to provide a useful function - especially in the context of electronic business - it does need access to some local resources. Simultaneously, the security policy of most organizations demands that local applications not be given open access to every resource. Therefore, the original JDK's trust model is too restrictive on applets and too permissive on local applications.

JDK 1.1 expanded the sandbox model via signed, trusted applets. Basically, the client who downloads the applet can choose to trust the originator. Assuming that the digital signature of the downloaded applet verifies correctly, the applet would have access to local resources (files, network connections, etc.). Therefore, a client can treat each applet differently based on its digital signature. However, the access control model for a given applet remains binary. If trusted, it has access to all resources and if not, it must run within the sandbox.

JDK 1.2 further expands the binary access control model of JDK1.1 with fine grain access control. JDK 1.2 introduces the concept of protection domains which allows a client to specify exactly which resources a given applet or application may access and for what reason (e.g., read a file, connect to a host, etc.). Therefore, JDK 1.2 allows for the correct implementation of two important and complementary security elements:

  1. Allowing a downloaded applet access to predefined local resources. That is, opening the sandbox as determined by the local security policy.
  2. Denying a local application access to predefined local resources. That is, restricting local applications from having access to all resources.

Protection Domains
A protection domain is a set of classes currently accessible by a principal. Interestingly enough, the original sandbox model represents a protection domain with a fixed, static boundary. Under the JDK 1.2 model, a security administrator can dynamically change the boundaries of a protection domain using permissions. Here's how it works. Each class belongs to one, and only one, protection domain. Associated with each domain is a set of permissions, reflecting the security policy of the organization. Figure 1 illustrates the relationship between classes, protection domains and permissions. As shown, the class publisher belongs to a protection domain called StockPub. Classes belonging to this domain can read and write a file called /usr/openhorizon/ambrosia/publogfile. Additionally, classes belonging to this domain have publish permission on a resource called stock.nyse. Subscriber and auditor classes belong to a protection domain designated by StockSub. Classes belonging to this domain can read and write a file called /usr/openhorizon/ambrosia/sublogfile. Additionally, these classes have subscribe permission to a resource called stock.nyse. Note: Even though a class cannot belong to more than one protection domain, a protection domain can be shared by multiple classes. Thus, a security administrator can logically group classes and assign them to a protection domain.

Figure 1
Figure 1:

Enforcing Access Control
So, who enforces the actual access control? It depends. The Java runtime environment controls external resources such as the file system, network connections, the keyboard and the mouse. It is the Java runtime environment that enforces access control to these resources. In the example illustrated in Figure 1, it is the Java runtime environment that decides auditor.class can read and write /usr/openhorizon/sublogfile, and that this class cannot delete the same file. This is because the administrator has explicitly granted read and write permission to the file but not delete permission. On the other hand, the Java runtime environment has no way to interpret or enforce application dependent resources. Again, in Figure 1, publish and subscribe permissions, and the resource represented by stock.nyse, are completely application dependent. (These operations are pertinent to an application built on top of a publish/subscribe middleware. The resource stock.nyse represents a subject for publishing information and subscribing to information.) Thus, each application must enforce access to its resources in the appropriate manner. For this reason, JDK 1.2 classifies resources according to two general categories: 1: system resources fall into the system domain; and, 2: application resources fall into the application domain. Figure 2 illustrates this classification.

Figure 2
Figure 2:

Crossing Protection Domains
We mentioned earlier that a class belongs to one and only one protection domain. Simultaneously, an application may need access to resources that belong to multiple domains. For example, in Figure 1, the file /usr/openhorizon/publogfile belongs to the system domain while the resource stock.nyse belongs to the application domain. It turns out that a single thread of execution usually traverses more than one class and, hence, it traverses more than one protection domain. For example, if a client downloads the applet publisher.class, it is likely that this class will publish a message and write to a log file. As a thread traverses multiple domains, it is crucial that it does not become more privileged. For example, a thread that originates in publisher.class may invoke a method from a class in the java.io package to write to a file. The write operation must occur in the system domain as it controls access to the file resource. However, publisher.class must not gain additional privileges (e.g., the ability to attempt to write to any file) as it enters the system domain. Conversely, a thread originating in the system domain may invoke a method in the application domain. For example, an AWT thread may call the applet's paint method. Again, it is crucial that the thread has its privilege reduced to match the permissions of the specific application domain. Luckily, the Java runtime environment correctly enforces this crucial security semantic. Thus, an application belonging to a less powerful domain cannot gain additional privileges as a result of invoking a method in a more powerful domain. Simultaneously, a thread originating from a more powerful domain loses some of its privileges when it calls a method in a less powerful domain. We can generalize this by stating that the permission of an execution thread is the intersection of the permissions granted to all protection domains traversed by that thread.

Steps Involved in Creating a Security Policy
In order to create an effective access control security policy, you must follow these steps:

  1. Determine the principals to whom you wish to grant certain permissions. Obtain a digital certificate for each principal. This is a one time effort that aids in verifying the origin of applets.
  2. If applicable, determine the URL codebase when the applet must originate. You may wish to grant certain permissions to an applet that is digitally signed by a principal but only if it originates from a specific URL.
  3. Define the set of resources for which you want to grant permissions. These could be system resources (e.g, files, network connections, etc.) or application specific resources.
  4. Define the exact set of permissions you would like to grant.
After you complete these tasks, you are ready to create a security policy file. A security policy file has a fairly straightforward format. As illustrated in Listing 1, each entry in the policy file begins with the reserved word "grant." Then, within each entry, the reserved word "permission" delimits the set of permissions granted.

Listing 2 shows progressively elaborate access control entries in a policy file. Lines 1-3 designate that any applet, originated from anywhere, can read and write the file /usr/tmp/logfile. This is because the grant line does not specify a SignedBy clause or a CodeBase clause. Note: java.io.FilePermission is a built-in permission in JDK 1.2; it must be specified using its fully qualified package name.

In Listing 2 (lines 5-8), we specify permissions for an applet that is digitally signed by a principal called "openhorizon." As shown, such an applet would be able to connect to a socket on the host "www.openhorizon.com" as long as the listening port on the host is in the range of 8000-8200. Again, note that that java.net.SocketPermission is a built-in permission in JDK1.2 and must be specified using its fully qualified package name.

In Listing 2 (lines 10-13), we specify permission for an applet that is digitally signed by the principal "openhorizon" and originates from www.openhorizon.com/Ambrosia/demo. As shown, such an applet would be able to connect to port 8506 on the host www.openhorizon.com. Additionally, the applet would have publish permission on the resource demo.nyse.stock. Note that com.openhorizon.client.PublishPermission is a custom permission defined by the package of the downloaded applet. Moreover, demo.nyse.stock is a resource defined by the package of the downloaded applet. You may have noticed that unlike the file/read and socket/connect examples, PublishPermission does not have a specific action. This is perfectly reasonable in the JDK 1.2 model; if a permission is granular enough, an action need not be specified.

Finally, Listing 2 (lines 15-19) shows a very elaborate entry in the policy file. Here, we grant certain permissions to applets that originate from the specified URL and are signed by "openhorizon." Using the signedBy clause in the permission entry (i.e., signedBy "OHISecurityOfficer"), we direct the Java runtime to verify the digital signature of the bytecode that implements the permission com.openhorizon.client.PublishPermission. This feature allows for a very powerful access control mechanism: it prevents permission spoofing.

Permission Classes in JDK 1.2
Three classes in JDK 1.2 form the basis of permissions:

  1. java.security.Permission is an abstract class. Its subclasses represent specific permissions (e.g., file access permission, network connect permission, etc.).
  2. java.security.PermissionCollection is an aggregate of homogenous permissions. It is useful for grouping similar permissions and granting all permissions as a group. For example, one can create a FileAccess permission collection that represents read, write and delete access to certain files. FileAccess would be a PermissionCollection that comprises three java.io.FilePermission classes: read, write and delete.
  3. java.security.Permissions holds a heterogeneous collection of permissions. In other words, if you wanted to group several different types of permission collections and grant them as a group, you would use this class. For example, you may define a TotalAccess permission that comprises FileAccess and NetAccess permission collections.
JDK 1.2 comes with a number of built-in permission classes. Figure 2 illustrates the three base classes and their relationship to built-in derived classes. Two important built-in permissions are explained here:
  1. java.io.FilePermission: This class is a subclass of java.security.Permission. It controls access to files and directories. This class allows wildcard specification for files. Listing 3 shows some examples of file permissions. As shown, P1 signifies a permission to read and write the specified file. P2 represents a permission to delete all files under the subtree /usr/ambrosia/log. P3 signifies a permission to execute any program under the directory /usr/ambrosia/bin. Finally, P4 represents a permission to read all files in the file system.
  2. java.net.SocketPermission: This class is a subclass of java.security.permission. It controls access to network resources. This class also provides for wildcard specification for host names and ports. Listing 3 shows some examples of network permissions. As shown, P5 signifies a permission to listen to port 8506 on the host demo.openhorizon.com. P6 represents a permission to connect to any host in the com domain.
Enforcing Application-Dependent Access Control
We mentioned earlier that the Java runtime environment enforces access to files, network resources, the keyboard, the mouse, etc. We also mentioned that the Java runtime environment has no way to interpret or enforce application dependent resources. Therefore, it is the application that must enforce access control to these type of resources. Fortunately, this is really easy. JDK 1.2 defines a class called AccessController. This class has a very important static method called checkPermission. All an application programmer needs to do is invoke this method and pass it the permission that needs to be checked. If the thread of execution has the given permission, as specified by the security policy file, then AccessControl.checkPermission returns quietly. Otherwise, this method throws an exception.

Java is rapidly evolving to a mature platform for building mission critical applications. Comprehensive security is an important element of mission critical applications. With the introduction of Protection Domains in JDK 1.2, Java developers have the means to implement an effective security policy for fine grain access control. Together with Java Cryptographic Architecture (JCA) and Java Cryptographic Extension (JCE), Protection Domains form the basis for building a comprehensive security package into the Java Development Kit.

About the Author
Jahan Moreh is a distributed system architect with Michigan Group, Inc. He specializes in middleware architecture and information security. You can reach him via e-mail at [email protected]


Listing 1: Security policy file format.
grant [signedBy “principal name of the signer of the applet”] 
[,codeBase “URL whence the code must originate”]  
  permission permission_class_name “target resource name” [, “action name”]  
[, signedBy “principal name of the signer of the permission”] ;  

 permission  ·..  

grant ·..  

Listing 2: Example of a security policy file.
1 grant {  
2  permission java.io.FilePermission “/usr/tmp/logfile”, “read, write”;  
3 };  
5 grant signedBy “openhorizon” {  
6  permission java.net.SocketPermission “www.openhorizon.com:8000-8200” ,   
7         “connect”;   
8 };  
10  grant signedBy “openhorizon ”, codeBase “http://www.openhorizon.com/Ambrosia/demo” {  
11 permission java.net.SocketPermission “www.openhorizon.com:8506” , “connect”;  
12 permission com.openhorizon.client.PublishPermission  “demo.stock.nyse”;  
13 };  
15 grant signedBy “openhorizon”, codeBase “http://www.openhorizon.com/Ambrosia/prod” {  
16 permission java.net.SocketPermission “www.openhorizon.com:8976” ,“connect”;  
17 permission com.openhorizon.client.GuaranteedPermission  “prod.stock.nyse”,  
18    signedBy “OHISecurityOfficer”;  
19 };  

Listing 3: FilePermission  and SocketPermission classes.
1 FilePermission P1 = new FilePermission (“/usr/ambrosia/log/logfile”, “read,write”);  
2 FilePermission P2 = new FilePermission (“/usr/ambrosia/log/-”, “delete”);  
3 FilePermission P3 = new FilePermission (“/usr/ambrosia/bin/*”,  “execute”);  
4 FilePermission P4 = new FilePermission (“-“, “read”);  
5 SocketPermission P5 = new SocketPermission (“demo.openhorizon.com:8506”, “listen”);  
6 SocketPermission P6 = new SocketPermission (“*.com”, “connect”);  

Listing 4: Implementing access control in an application.
1 com.openhorizon.client.PublishPermission publishPerm;  
2 publishPerm = new com.openhorizon.client.PublishPermission(“demo.stock.nyse”);  
3 try {  
4 AccessController.checkPermission (publishPermission);  
5 } catch (java.security.AccessControl.Exception accessViolation) { ·.. }

All Rights Reserved
Copyright ©  2004 SYS-CON Media, Inc.
  E-mail: [email protected]

Java and Java-based marks are trademarks or registered trademarks of Sun Microsystems, Inc. in the United States and other countries. SYS-CON Publications, Inc. is independent of Sun Microsystems, Inc.