Principle of Least Privilege

According to the principle of least privilege, every program and every user of the system should operate using the least set of privileges necessary to complete their particular task [Saltzer 1974, Saltzer 1975]. The Build Security In website [DHS 2006] provides additional definitions of this principle. Executing with minimal privileges mitigates against exploitation in case a vulnerability is discovered in the code. These principles can be applied in various ways to Java language programming. Occasionally a system will have components, most of which require only a base set of privileges, but a few require more privileges than the base set; these are said to run with elevated privileges.

Only code that requires elevated privileges should be signed; other code should not be signed. (See rule ENV00-J. Do not sign code that performs only unprivileged operations.) The security policy that defines the set of permissions should be as restrictive as possible. When a Java program is run with a security manager in place, the default security policy file grants permissions sparingly, however, Java's flexible security model allows the user to grant additional permissions to applications by defining a custom security policy. Specific rules that enforce this principle include:

Code that needs to be signed can coexist with unsigned classes in the same JAR file. It is recommended that all privileged code be packaged together. (See rule ENV01-J. Place all security-sensitive code in a single JAR and sign and seal it for more information.) Furthermore, it is possible to grant privileges to code on the basis of the code base and/or its signer using a security policy.

Privileged operations should be limited to the smallest possible code blocks that require such privileges. The Java AccessController mechanism allows only certain parts of code to acquire elevated privileges. When a class needs to assert its privileges, it executes the privileged code in a doPrivileged() block. The AccessController mechanism works in conjunction with the security policy in effect. Because users may be unaware of the details of the security model and incapable of correctly configuring security policies tailored to their requirements, privileged code present within the doPrivileged() blocks must be kept to a minimum to avoid security vulnerabilities.

Security Manager

A SecurityManager is a Java class that defines a security policy for Java code. This policy specifies actions that are unsafe or sensitive. Any actions not allowed by the security policy cause a SecurityException to be thrown. Code can also query its security manager to discover which actions are allowed. The security manager can also be used to control the functions the trusted Java API can perform. When untrusted code should be disallowed from accessing system classes, it should be granted specific permissions to prevent it from accessing trusted classes in the specified packages. The accessClassInPackage permission provides the required functionality.

There are several predefined security managers available for certain types of applications. The applet security manager is used to manage all Java applets. It denies applets all but the most essential privileges. It is designed to prevent inadvertent system modification, information leakage and user impersonation. The use of security managers is not limited to client side protection. Webservers, such as Tomcat and Websphere, use this facility to isolate trojan servlets and malicious JSP code, as well as to protect sensitive system resources from inadvertent access.

For Java applications that run from the command line, a default or custom security manager can be set using a special flag. Alternatively, it is possible to install a security manager programmatically. Installing a security manager programmatically helps create a default sandbox that allows or denies sensitive actions based on the security policy in effect.

From Java 2 SE Platform onwards, the SecurityManager class is non-abstract. As a result, there is no explicit requirement of overriding its methods. To create and use a security manager programmatically, the code must have the runtime permissions createSecurityManager (to instantiate SecurityManager) and setSecurityManager to install it. These permissions are checked only if a security manager is already installed. This is useful for situations where there is a global-default security manager in place, such as on a virtual host, and individual hosts need to be denied the requisite permissions for overriding the default security manager with a custom one.

The security manager is closely tied to the AccessController class. The former is used as a hub for access control whereas the latter is the actual implementer of the access control algorithm. The security manager supports

  • Providing backward compatibility: Legacy code often contains custom implementations of the security manager class because it was originally abstract.
  • Defining custom policies: Subclassing the security manager permits definition of custom security policies (for example, multilevel, coarse, or fine grain).

Regarding the implementation and use of custom security managers, as opposed to default ones, the Java Security Architecture Specification [SecuritySpec 2008] states

We encourage the use of AccessController in application code, while customization of a security manager (via subclassing) should be the last resort and should be done with extreme care. Moreover, a customized security manager, such as one that always checks the time of the day before invoking standard security checks, could and should utilize the algorithm provided by AccessController whenever appropriate.

Many of the Java SE APIs perform security manager checks by default before performing sensitive operations. For example, the constructor of class java.io.FileInputStream throws a SecurityException if the caller does not have the permission to read a file. Because SecurityException is a subclass of RuntimeException, the declarations of some API methods (for example, those of the java.io.FileReader class) may lack a throws clause that lists the SecurityException. Avoid depending on the presence or absence of security manager checks that are not specified in the API method's documentation.

Class Loader

The java.lang.ClassLoader class and its descendent classes are the means by which new code is dynamically loaded into the JVM. Every class provides a link to the ClassLoader that loaded it; furthermore every class loader class also has its own class that loaded it, on down to a single 'root' class loader. ClassLoader itself is abstract, so it cannot be instantiated. All class loaders inherit from SecureClassLoader, which itself inherits from ClassLoader. SecureClassLoader performs security checks on its members, as do its descendents. It defines a getPermissions() method, which indicates the privileges available to classes loaded by the class loader, This serves to provide protection mechanisms limiting what additional classes may be loaded by untrusted code.

Misc.

This is more classloader stuff that isn't strictly necessary to understand the rules

Class loaders, as well as some other sensitive classes, have the ability to modify or completely avoid security manager access controls. Many class loaders check package access permissions before attempting to load a class (see table below). However, instantiating a URLClassLoader using either of its constructors bypasses the call to the security manager's checkPackageAccess() method. Although the package access check is an optional step (no Oracle-manufactured URL class loader performs it), it is a good idea to ensure that the program is actually allowed to access the class being loaded.

According to the Java API [java:API 2006] the ClassLoader.checkPackageAccess() method documentation:

Throws a SecurityException if the calling thread is not allowed to access the package specified by the argument. This method is used by the loadClass method of class loaders. This method first gets a list of restricted packages by obtaining a comma-separated list from a call to java.security.Security.getProperty("package.access") and checks to see if pkg starts with or equals any of the restricted packages. If it does, then checkPermission gets called with the RuntimePermission("accessClassInPackage."+pkg) permission.

In 2004, Schoenefeld [java:Schoenefeld 2004] discovered a vulnerability in Opera v7.54 in that the default security policy granted the runtime permission "accessClassInPackage.sun.*" to unprivileged applets so that they could access internal Sun packages. This allowed attackers to obtain sensitive local information and crash the client web browser.

The following table shows which class loaders check package access permissions and which do not:

Class loader

Performs Access Checks

bootstrap

No

extensions

No

system

Yes

URLClassLoader

Maybe*

* A URLClassLoader, when constructed using the default constructor, does not check package access permissions. However, when the static newInstance() method is used, the obtained instance carries out the check. Doing so does not limit what system classes can do; however, it restricts the range of system packages that can be used from less-privileged code.

When any method from the following table is invoked on a Class, ClassLoader or Thread object, a comparison is run between the method's immediate caller's class loader and that of the object on which the method is invoked. (SCG 2007)

APIs capable of bypassing SecurityManager's checks

Class.newInstance()

Class.getClassLoader()

Class.getClasses()

Class.getField(s)

Class.getMethod(s)

Class.getConstructor(s)

Class.getDeclaredClasses()

Class.getDeclaredField(s)

Class.getDeclaredMethod(s)

Class.getDeclaredConstructor(s)

ClassLoader.getParent()

ClassLoader.getSystemClassLoader()

Thread.getContextClassLoader()

As an example of what constitutes the immediate caller and the object, consider the method java.lang.Class.newInstance(). Here, the immediate caller is the class that contains this method call whereas the object on which the newInstance() method is being invoked is referred to as the Class object (classObjectName.newInstance()). According to the Java Language Specification [JLS 2005], the method getClass() returns the Class object that represents the class of the object.

If a security manager is present, untrusted code that does not have the permissions to use the API directly is prevented from indirectly using trusted code containing the API call to perform the operation. However, the security manager checks are bypassed if the class loader of the immediate caller is the same as or the delegation ancestor of the class loader of the object on which the API is invoked. Consequently, untrusted callers who do not have the required permissions but are capable of passing the class loader check are able to perform sensitive operations if the trusted code invokes these APIs on their behalf.

The Java SE 6 class loader delegation hierarchy shown below summarizes several cases and highlights those where security checks are bypassed:

Immediate Caller

ICC*

Class Object

COC**

Class Loader Check

Security Check

C1

A

C2, C3, C4, C5

Application, B, C

A is not a delegation ancestor of Application, B or C

Yes

C2

Application

C1

A

Application is not a delegation ancestor of A

Yes

C2

Application

C3, C4, C5

B and C

Application is a delegation ancestor of B and C

No

C3

B

C4

B

The class loader is same for C3 and C4 (B)

No

C4

B

C3

B

The class loader is same for C4 and C3 (B)

No

C5

C

C1, C2, C3, C4

Application, A, B, C

C is not a delegation ancestor of Application, A, B or C

Yes

* ICC: Intermediate caller's class loader
** COC: Class object's class loader

Care must be taken when using these APIs that trusted code does not accept Class objects from untrusted code for further use. For example, if trusted code is loaded by the bootstrap class loader, it can create an instance of a sensitive system class by using the newInstance() method on the Class object. If the method that creates the instance is visible to untrusted code, no security manager checks are carried out to prohibit the untrusted code from indirectly creating the class instance (untrusted code must pass the class loader comparison check).

Similarly, instances of trusted Class objects should not be returned to untrusted code. An untrusted caller can invoke the affected APIs and bypass security checks if its class loader is the same as or the delegation ancestor of the trusted code's class loader.

The table also shows APIs that use the ClassLoader class object. Class loaders facilitate isolation of trusted components from untrusted ones. They also ensure that the untrusted components do not interfere with each other. The proper choice of the class loader to load a class is of utmost importance. Using untrusted class loaders for performing operations of sensitive nature in trusted code can result in vulnerabilities.

With respect to the ClassLoader object APIs, security manager checks may also get bypassed depending on the immediate caller's class loader. Consider for instance, the ClassLoader.getSystemClassLoader() and ClassLoader.getParent() methods that operate on a ClassLoader object. In the presence of a security manager, these methods succeed only if the immediate caller's class loader is the delegation ancestor of the current ClassLoader object's class loader or if the immediate caller's class loader is the same as the current ClassLoader object's class loader or if the code in the current execution context has the getClassLoader RunTimePermission.

Untrusted code can bypass the security checks if its class loader is either the same or a delegation ancestor of the trusted code's class loader. Consequently, care should be taken while specifying the parent of a trusted class loader. Likewise, trusted code must not use any class loader instance supplied by untrusted code. For instance, a class loader instance obtained from untrusted code may never be used to load a trusted class that performs some sensitive operation. Also, a trusted class loader that performs security sensitive operations must never be made available to untrusted code by returning its instance.

3 Comments

  1. The Misc stuff can mostly be discarded. A lot of it came from SEC03-J, and some of it might belong there.

  2. It is designed to protect inadvertent system modification, information leakage and user impersonation.

    should be written to:
    It is designed to protect system from inadvertent modification, information leakage and user impersonation.

    1. Thanks. I s/protect/prevent/;