Prior to Java 2, the runtime security model used a very tightly constrained sandbox model (sandbox). Readers should be familiar with the Java untrusted Applet code that provides runtime security checks based on this tightly restricted sandbox model. The essence of the sandbox model is that any locally run code is trusted and has full permissions to access critical system resources. For applets, it is an untrusted code that can access only limited resources within the sandbox scope. Of course, you can configure your Applet as a trusted code by digitally signing, with the same permissions as the local code.
Starting with Java 2, Java provides a run-time security model based on policy and stack authorization, which is a finer grained access control, easy to configure and extend, as shown in the overall architecture 1:
Figure 1. Java 2 Security Model
Simply put, when a class is loaded into the JVM by a class loader, these runtime classes are given different permissions according to the configuration of the Java Policy file (Loader). When these classes are accessing certain system resources (such as opening sockets, reading and writing files, etc.), or performing certain sensitive operations (such as access passwords), Java The check permissions method of the Security Manager (Ava.lang.SecuirtyManager) is called to check whether these classes have the necessary permissions to perform the operation.
Before continuing in-depth discussion, let's first clarify a few concepts:
- policy , which is a system security policy, configured by a user or administrator to configure permissions to execute code. The run-time Java.security.Policy object is used to represent the policy file.
- Permissions , Java defines a hierarchy of permission objects, and the root class of all permission objects is java.security.Permission. The definition of a permission involves two core attributes: Target and action (action). For example, for a file-related permission definition, the object is a file or directory whose actions include: Read, write, delete, etc.
- a protected domain that a protected domain can understand as a collection of classes that have a common set of permissions.
In Java 2, permissions are actually given to the protected domain, not directly to the class. Mappings between permissions, protection domains, and classes 2.
Figure 2. classes, protecting domains, mapping relationships for permissions
As shown in 2, the current run-time stack is from A.class to E.class. Each frame on the run-time stack (the stack frame) is classified by Java as a protection domain (the protection domain is built by Java based on the Policy file configuration). When the security manager of Java performs a permission check, it checks each stack frame on it for permission to execute if and only if the permission set given by each stack frame implies (imply) the required permission, otherwise The java.security.AccessControlException exception will be thrown, and the operation execution fails.
There are a few things to note about the Java 2 security model:
- The model is based on the stack authorization, which is also applicable in multi-threaded environment. For example, when a parent thread creates a child thread, the execution of the child thread is considered a continuation of the parent thread's execution, so the Java security Manager checks the run-time stack, including both the current child thread and the run-time stack inherited from the parent thread, when the permission is checked. This means that it is not possible for a user to get additional permissions through the creation of a thread.
- Java developers can use accesscontroller.doprivileged to optimize the additional performance overhead associated with permission checks. As shown in 3, the permission check for Java starts at the top of the stack, down to the bottom of the stack, until it touches the Doprivileged method call, or until it reaches the base. Using doprivileged can avoid unnecessary stack traversal (stack Traverse) and improve the performance of the program.
- In this model, there is a special protection domain, system domain. All classes loaded by the null class loader are called system code and automatically have all the permissions. Moreover, all important protected external resources, such as file systems, networks, screens, keyboards, etc., can only be obtained through system code.
Figure 3. Doprivileged Stack Frame
Next, this article will give a simple example, and then we'll go further in this example to create an application for secure collaboration between threads.
Example
Our example is simple: The client invokes the API provided by Logservice and writes the Message to the disk file.
Listing 1. Client programs
Package sample.permtest.client; ... public class Client { ... public static void Main (string[] args) { //constructs the message log and writes it to the C:\\paper\\client\\out.tmp file using Logservice. Message message = new Message ("C:\\paper\\client\\out.tmp", "Hi, this was called from client" + ' \ n '); LogService.instance.log (message); Constructs the message log and writes it to the C:\\paper\\server\\out.tmp file using Logservice. message = new Message ("C:\\paper\\server\\out.tmp", "Hi, this was called from client" + ' \ n '); LogService.instance.log (message);} }
Listing 2. Logservice
Package Sample.permtest.server;......public class Logservice { ... public void log (message message) { final String destination = Message.getdestination (); Final String Info = Message.getinfo (); FileWriter FileWriter = null; Try { filewriter = new FileWriter (destination, true); Filewriter.write (info); Filewriter.close (); } catch (IOException IOException) { ioexception.printstacktrace ();}} }
As shown in listings 1 and 2, this is a common Java application. We put this program in the Java security model to execute. The Client class is placed in the Client.jar jar package, and the Logservice class is placed in the Server.jar jar package.
First we use the Keytool tool to generate the KeyStore files we need, as well as the required digital certificates, as shown in Listing 3.
Listing 3. Generate KeyStore files and their digital certificates
>keytool-genkey-alias client -keyalg rsa-keystore C:\paper\.keystore >keytool-genkey-alias Server - Keyalg Rsa-keystore C:\paper\.keystore
In Listing 3, we generate the C:\paper\.keystore file, which uses the RSA algorithm to generate two digital certificates that are aliased to client and server. (Note: The key for KeyStore and client,server certificates is 111111 for convenience)
We use the command shown in Listing 4 to sign Client.jar and Server.jar.
Listing 4. Signature JAR File
>jarsigner.exe -keystore C:\paper\.keystore -storepass 111111 c:\paper\client.jar Client >jarsigner.exe -keystore C:\paper\.keystore -storepass 111111 c:\paper\server.jar Server
In Listing 4, we signed the Client.jar file with a digital certificate with the alias client, and signed the Server.jar file with the digital certificate of the alias server.
Use the graphical tool Policytool.exe to create the Policy file shown in Listing 5.
Listing 5. Policy file
/* Automatically GENERATED on Thu 15:40:25 CST 2009*//* Do not EDIT */KeyStore "File:////C:/paper/.keystore"; Grant Signedby "Client" { permission Java.io.FilePermission "c:\\paper\\client\\*", "Read,write";}; Grant Signedby "Server" { permission java.security.AllPermission;};
The Policy file states that all code that is signed by "client" has permission to read and write all files in the "c:\\paper\\client\\" directory, while all code signed by "Server" has all the permissions. Java will create the appropriate protection domain according to the policy file according to the signer.
Everything is ready, we run the code, as shown in Listing 6.
Listing 6. Run the program
>java-djava.security.manager -djava.security.policy=my.policy-classpath Client.jar;server.jar Sample.permtest.client.Client
There are two runtime options that are particularly important,-djava.security.manager tells the JVM to load the Java security Manager, carries out run-time safety checks, and-djava.security.policy is used to specify the policy file we use.
The result of the run is shown in Listing 7.
Listing 7. Run results
Exception in thread "main" Java.security.AccessControlException:access denied (java.io.FilePermission c:\paper\server \out.tmp write) at java.security.AccessControlContext.checkPermission (Unknown Source) at Java.security.AccessController.checkPermission (Unknown Source) at java.lang.SecurityManager.checkPermission ( Unknown source) at Java.lang.SecurityManager.checkWrite (Unknown source) at java.io.fileoutputstream.< Init> (Unknown source) at java.io.fileoutputstream.<init> (Unknown source) at Java.io.FileWriter. <init> (Unknown Source) at sample.permtest.server.LogService.log (logservice.java:19) at Sample.permtest.client.Client.main (CLIENT.JAVA:16)
After the client runs, the first message is successfully written to the C:\\paper\\client\\out.tmp file, and the second message is rejected because there is no write permission to the c:\paper\server\out.tmp file.
Back to top of page
Secure Collaboration between threads
The example given in the previous section will complicate situations in an environment where asynchronous collaboration between threads is placed. As shown in 4.
Figure 4. Asynchronous Collaboration of Threads
4, in such a scenario, the run-time stack of the client thread is completely independent of the server-side thread, and they are only collaborating asynchronously through a shared data structure message queue. For example, when the client thread puts a message x, and then the server-side thread takes the message x for processing, we still assume that the message x is the expected server thread to write the messages to the C:\paper\server\out.tmp file. At this time, how can the service program ensure that the client has permission to write to the c:\paper\server\out.tmp file?
Java provides a solution based on a thread collaboration scenario, as shown in Listing 8:
Listing 8. Logservice of the thread collaboration version
Package Sample.permtest.thread.server;......public class Logservice implements runnable{... public synchronized void log (Message message) {//The method will execute in the client thread environment//When the message is put into the queue, we get the execution environment of the client thread through//accesscontroller.getcontext (),//and save it in time To. Message.m_accesscontrolcontext = Accesscontroller.getcontext (); _messagelist.add (message); Notifyall (); } ...//Remove the message from the queue and process the public void run () {while (true) {message message = NULL; try {message = Retrievemessage (); } catch (Interruptedexception interruptedexception) {break; } final String Destination = Message.getdestination (); Final String stringmessage = Message.getinfo (); Accesscontroller.doprivileged (New Privilegedaction () {Publi C Object Run () { FileWriter FileWriter = null; try {filewriter = new FileWriter (destination, true); Filewriter.write (Stringmessage); Filewriter.close (); } catch (IOException IOException) {IOEXCEPTION.P Rintstacktrace (); } return null; }}, Message.m_accesscontrolcontext//The client thread at the time the execution environment passed in, for permission checks. ); } }}
The M_accesscontrolcontext member variable of the message class is a AccessControlContext object that encapsulates the execution environment snapshot of the current thread, which we can call Accesscontroller by GetContext method is obtained. Secure thread collaboration works as shown in 5.
Figure 5. Thread asynchronous collaboration permission check path
The arrows in Figure 5 indicate the path to the Java Security Manager permission check, starting at the current frame, and checking the runtime stack along the server thread until the accesscontroller.doprivileged frame is encountered. Because when we call the Doprivileged method, we pass in the M_accesscontrolcontext, which is the execution environment where the client thread thread inserts the message into the message queue, so the Java security manager jumps to the execution environment. The execution stack checks each time the message is inserted along the client.
In this section of the thread version of the Log service implementation, the Client class in the Sample.permtest.thread.client package is exported as a Thread_client.jar jar package, and Logservice in Sample.permte In the St.thread.server package, the package is exported as a Thread_server.jar jar package. The package signature for this section, like the previous section, uses the same digital certificate as the previous section.
For complete source code, readers can download it in the resources list later in this article.
Back to top of page
Summary
This article gives an exhaustive description of the security model characteristics of the Java 2 runtime, and how to build a secure thread collaboration application based on this model. It is worth mentioning that when your Java application uses Java 2 to provide the runtime security model, the performance of the program is reduced is inevitable, because we have seen that the Java 2 security model is based on the stack authorization, which means that every time the Java Security Manager checks the execution of the permission method, All frames that are currently running on the rows stack are traversed to determine whether the permission requirements are met. So your design must be a choice between safety and performance. Of course, once you have applied the Java security model, you still have the opportunity to optimize performance, such as using the Doprivileged method to remove unnecessary stack traversal, and further, you can inherit the Java.lang according to the characteristics of your application. SecurityManager class to develop a security manager for your application.
Original address: http://www.ibm.com/developerworks/cn/java/j-lo-rtsecurity/
Appendix, Java Permission Control code:
java.security.AccessController.doPrivileged ( new java.security.PrivilegedExceptionAction <Void>() { @Override public Void Run () throws Servletexception, IOException { internaldofilter (req,res); return NULL ; } } );
Thread collaboration based on the Java 2 runtime security model--go