A security policy specifies what permissions–which types of system resource accesses–are allowed for code from specified code sources. A code source (an object of type CodeSource) 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 sign the code (if it was 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 keys and from the same URL are placed in the same domain, and 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 applet or application runs in its appropriate domain, determined by its code source. For an applet (or an application running under a security manager) to perform a secured action such as reading or writing a file, the applet or application must be granted permission for that particular action. Privileged code can temporarily access resources that are normally unavailable to the caller. This is necessary, for example, if a system utility needs to open a font file to display a document, on behalf of the user, but the application does not have permission to do so. To perform this action, the system utility becomes privileged while obtaining the fonts.
Code blocks can be made privileged using the AccessController.doPrivileged() method. Privileged code runs with with all the privileges of the protection domain associated with the code source. Typically, these privileges exceed those required to perform the privileged operation. Ideally, code should only be granted the minimum set of privileges required to complete its operation.
This guideline addresses the problem of excess privileges. See SEC56-JG. Define custom security permissions for fine grained security for another approach to solving this problem.
Noncompliant Code Example
This noncompliant code example shows a library method that allows callers to perform a privileged operation (reading a file), using the wrapper method performActionOnFile().
private FileInputStream openFile() {
final FileInputStream f[] = { null };
AccessController.doPrivileged(new PrivilegedAction() {
public Object run() {
try {
f[0] = new FileInputStream("file");
} catch(FileNotFoundException fnf) {
// Forward to handler
}
return null;
}
});
return f[0];
}
// wrapper method
public void performActionOnFile() {
openFile();
}
In this example, the trusted code grants privileges beyond those required to read a file. However, the doPrivileged() code block only requires read access to the file. 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 the protection domain and those of the context passed as the second argument. Consequently, a caller that wishes to only grant permission to read the file can provide a context that has only the file read permission.
An AccessControlContext that grants the appropriate file read permission can be created as an inner class.
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 FileInputStream("file");
} catch (FileNotFoundException fnf) {
// Forward to handler
}
return null;
}
}, context); // Restrict the privileges by passing the context argument
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)});
}
}
// Wrapper method
public void performActionOnFile() {
openFile(FileAccessControlContext.INSTANCE); // only grant open-for-reading privileges
}
Alternatively, an appropriate AccessControlContext can be obtained using AccessController.getContext() if the caller does not have privileges to create it.
In this example, the two-argument form is preferred because the openFile() method does not know whether the caller should be granted read, write, or both permissions. In the special case where all the callers are known (for example, when all are private methods of a private class), the single argument method AccessController.checkPermission(permission) offers improved simplicity and performance. The two argument form should be used for all cases where there may be unknown callers. The performActionOnFile() method does not need to be declared private, provided permissions are restricted by accepting a context argument.
Applicability
Failure to follow the principle of least privilege can result in untrusted code performing unintended privileged operations. However, carefully restricting privileges adds complexity. This added complexity and reduced maintainability must be traded-off against any security improvement.
Related Guidelines
| MITRE CWE | CWE ID 272, "Least Privilege Violation" |
Bibliography
[API 2011] Class AccessController