A Java security policy grants permissions to code to allow access to specific system resources. A code source (an object of type CodeSource), to which a permission is granted, consists of the code location (URL) and a reference to the certificate(s) containing the public key(s) corresponding to the private key(s) used to digitally sign the code. Reference to the certificate(s) is pertinent only if the code was digitally signed. A protection domain encompasses a CodeSource and the permissions granted to code from that CodeSource, as determined by the security policy currently in effect. Consequently, classes signed by the same key and originating from the same URL are placed in the same protection domain. A class belongs to one and only one protection domain. Classes that have the same permissions but are from different code sources belong to different domains.
Each Java class runs in its appropriate domain, as determined by its code source. For any code running under a security manager to perform a secured action such as reading or writing a file, the code must be granted permission to perform that particular action. Privileged code can access privileged resources on behalf of an unprivileged caller by using the AccessController.doPrivileged() method. This is necessary, for example, when a system utility needs to open a font file on behalf of the user to display a document, but the application lacks permission to do so. To perform this action, the system utility uses its full privileges for obtaining the fonts, ignoring the privileges of the caller. Privileged code runs with all the privileges of the protection domain associated with the code source. These privileges often exceed those required to perform the privileged operation. Ideally, code should be granted only the minimum set of privileges required to complete its operation.
SEC53-J. Define custom security permissions for fine-grained security describes another approach to eliminating excess privileges
According to the principle of least privilege, code should not be granted more privileges than required for performing a task. This means that sections of code that require elevated privileges should be kept to a minimum. McGraw and Felten enlist various goals of the principle of least privilege in the context of the Java programming language:
- We want to grant each applet or application the minimum privileges it needs.
- Rather than assigning a given applet's entire collection of privileges to all of its classes, we want each class to get just what it needs.
- We want a class's privileges to be "turned off" except for brief periods of time.
- We even want to reduce the privileges of some of the built-in system classes.
The ways in which these goals can be achieved are discussed below.
Granting an applet the minimum privileges it needs is achieved by not signing it when it performs only unprivileged operations (ENV00-J. Do not sign code that performs only unprivileged operations). For applications, some additional steps may be required depending on the security policy. The default security policy file is restrictive however, the security model allows the user to grant additional permissions to applications by using a custom security policy. Several guidelines deal with granting or suppressing permissions (for instance, ENV00-J. Do not sign code that performs only unprivileged operations, ENV31-J. Never grant AllPermission to untrusted code and ENV32-J. Do not grant ReflectPermission with target suppressAccessChecks).
There are several ways to practice privilege separation. Applets or applications that need to be signed can coexist with unsigned classes in the same package (or JAR file). Furthermore, it is possible to grant privileges to code on the basis of the codebase or its signer.
The third goal, is realized by using the AccessController mechanism. Only certain parts of code require elevated privileges and this mechanism allows just that. When a class needs to enable its own privileges, it calls the sensitive methods in a doPrivileged block. It is critical that these privileges be revoked as soon as the sensitive operations have concluded. Users most often do not understand complex security policies nor do they understand code signing. They cannot be relied upon to restrict permissions in non-default policy files. Therefore, privileged code must be written with utmost care assuming that the user is incapable of correctly configuring the security policy.
The fourth goal can be achieved in part, by granting code specific permissions and protecting it from accessing classes in certain packages. This can be achieved by using the accessClassInPackage permission (SEC10-J. Do not allow the unauthorized construction of sensitive classes). Doing so does not limit what system classes can do, however, it allows restricting the range of system packages that can be invoked from less-privileged code.
Noncompliant Code Example
This noncompliant example includes nonprivileged code following the privileged openPasswordFile statement, in a doPrivileged block. This is a violation of the principle of least privilege. code example shows a library method that allows callers to perform a privileged operation (reading a file) using the wrapper method performActionOnFile():
| Code Block | ||
|---|---|---|
| ||
private FileInputStream openFile() { final FileInputStream f[] = { null }; AccessController.doPrivileged(new PrivilegedAction() { public Object run() { try { f[0] = openPasswordFile(password_filenew FileInputStream("file"); // call the privileged method here } catch(FileNotFoundException fnf) { // other operations Forward to handler } return null; }catch(FileNotFoundException cnf) { System.err.println("Error: Operation could not be performed"); } return null; } }); |
Compliant Solution
});
return f[0];
}
// Wrapper method
public void performActionOnFile() {
try (FileInputStream f = openFile()){
// Perform operation
} catch (Throwable t) {
// Handle exception
}
}
|
In this example, the trusted code grants privileges beyond those required to read a file, even though read access to the file was the only permission needed by the doPrivileged() block. Consequently, this code violates the principle of least privilege by providing the code block with superfluous privileges.
Compliant Solution
The two-argument form of doPrivileged() accepts an AccessControlContext object from the caller and restricts the privileges of the contained code to the intersection of privileges of the protection domain and those of the context passed as the second argument. Consequently, a caller that wishes to grant only permission to read the file can provide a context that has only the file-reading permissions.
An AccessControlContext that grants the appropriate file-reading permissions can be created as an inner class:It is critical to restrict the code that requires elevated privileges to a minimum. Privileged code must also be audited more carefully than non-privilege code, to ensure than there are no other security vulnerabilities. This is essential to prevent the misuse of privileges.
| Code Block | ||
|---|---|---|
| ||
private FileInputStream openFile(AccessControlContext context) { if (context == null) { throw new SecurityException("Missing AccessControlContext"); } final FileInputStream f[] = { null }; AccessController.doPrivileged( new PrivilegedAction() { public Object run() { try { f[0] = new openPasswordFileFileInputStream(password_"file"); } catch (FileNotFoundException fnf) { // call the privileged method here }catch(FileNotFoundException cnf) { System.err.println("Error: Operation could not be performed"); } return null Forward to handler } return null; } }, // Restrict the privileges by passing the context argument context); return f[0]; } private static class FileAccessControlContext { public static final AccessControlContext INSTANCE; static { Permission perm = new java.io.FilePermission("file", "read"); PermissionCollection perms = perm.newPermissionCollection(); perms.add(perm); INSTANCE = new AccessControlContext(new ProtectionDomain[] { new ProtectionDomain(null, perms)}); } }); // other operations |
...
// Wrapper method
public void performActionOnFile() {
try (final FileInputStream f =
// Grant only open-for-reading privileges
openFile(FileAccessControlContext.INSTANCE)) {
// Perform action
} catch (Throwable t) {
// Handle exception
}
}
|
Callers that lack permission to create an appropriate AccessControlContext can request one using AccessController.getContext() to create the instance.
Applicability
Failure to follow the principle of least privilege can lead to privilege escalation attacks when a vulnerability is exploited.
Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
|---|---|---|---|---|---|
SEC00-J | high | probable | high | P6 | L2 |
Automated Detection
TODO
Related Vulnerabilities
Search for vulnerabilities resulting from the violation of this rule on the CERT website.
References
| Wiki Markup |
|---|
\[[MITRE 09|AA. Java References#MITRE 09]\] [CWE ID 272|http://cwe.mitre.org/data/definitions/272.html] |
result in untrusted, unprivileged code performing unintended privileged operations. However, carefully restricting privileges adds complexity. This added complexity and the associated reduction of maintainability must be traded off against any security improvement.
Bibliography
...
SEC33-J. Define wrappers around native methods 01. Platform Security (SEC) SEC01-J. Provide sensitive mutable classes with unmodifiable wrappers