The java.security.AccessController
class is part of Java's security mechanism, and it is responsible for enforcing whatever security policy is applied to code. This class's static method doPrivileged()
can be used to execute a block of code while relaxing a security policy. So the block of code effectively runs with elevated privileges.
Consequently, any method that contains a doPrivileged()
method call must assume responsibility for enforcing its own security on the code block supplied to doPrivileged()
. Likewise any code in the {[doPrivileged()}} method must take care to prevent sensitive information from leaking out of a trust boundary. This may indicate that said information must not escape from the doPrivileged()
block itself, or it may be permitted within part of the application and excluded from other parts.
For example, suppose a web application must maintain a sensitive file of passwords for a web service, and suppose it must run untrusted code as well. The application could then enforce a security policy preventing most of itself, including any untrusted code, from accessing the sensitive file. But since it must also provide mechanisms for adding and changing passwords, it can use the doPrivileged()
method to temporarily bypass its own security policy for the purpose of managing passwords. In this case, any doPrivileged()
block must prevent any information about passwords from being accessible to untrusted code.
Noncompliant Code Example
In this noncompliant code example, the doPrivileged()
method is called from the openPasswordFile()
method. The openPasswordFile()
method is privileged and returns a FileInputStream
for the sensitive password file to its caller. Because the method is public, it could be invoked by an untrusted caller.
public class PasswordManager { public static void changePassword() throws FileNotFoundException { FileInputStream fin = openPasswordFile(); // test old password with password in file contents; change password } public static FileInputStream openPasswordFile() throws FileNotFoundException { final String password_file = "password"; FileInputStream fin = null; try { fin = AccessController.doPrivileged( new PrivilegedExceptionAction<FileInputStream>() { public FileInputStream run() throws FileNotFoundException { // Sensitive action; can't be done outside doPrivileged() block FileInputStream in = new FileInputStream(password_file); return in; } }); } catch (PrivilegedActionException x) { Exception cause = x.getException(); if (cause instanceof FileNotFoundException) { throw (FileNotFoundException)cause; } else { throw new Error("Unexpected exception type", cause); } } return fin; } }
Compliant Solution
In general, when any method containing the doPrivileged()
block exposes a field (such as a reference) beyond its own boundary, it becomes trivial for untrusted callers to exploit the program.
This compliant solution mitigates the vulnerability by declaring openPasswordFile()
to be private. Consequently, an untrusted caller can call changePassword()
but cannot directly invoke openPasswordFile()
.
public class PasswordManager { public static void changePassword() throws FileNotFoundException { // ... } private static FileInputStream openPasswordFile() throws FileNotFoundException { // ... } }
Compliant Solution (Hiding Exceptions)
The previous noncompliant code example and compliant solution would both throw a FileNotFoundException
if the password file did not exist. But suppose that the existence of the password file is considered sensitive information, and should not be thrown to potentially untrusted code?
This compliant solution suppresses the exception, using a null return value to indicate that the file does not exist. It uses the simpler PrivilegedAction
class rather than PrivilegedExceptionAction
, because it does not allow any exceptions to propagate out of the doPrivileged()
block.
class PasswordManager { public static void changePassword() { FileInputStream fin = openPasswordFile(); if (fin == null) { // no password file; handle error } // test old password with password in file contents; change password } private static FileInputStream openPasswordFile() { final String password_file = "password"; final FileInputStream fin[] = { null }; AccessController.doPrivileged( new PrivilegedAction() { public Object run() { try { // Sensitive action; can't be done outside doPrivileged() block fin[0] = new FileInputStream(password_file); } catch (FileNotFoundException x) { // report to handler } return null; } }); return fin[0]; } }
Risk Assessment
Returning references to sensitive resources from within a doPrivileged()
block can break encapsulation and confinement. Any caller who can invoke the privileged code directly and obtain a reference to a sensitive resource or field can maliciously modify its elements.
Guideline |
Severity |
Likelihood |
Remediation Cost |
Priority |
Level |
---|---|---|---|---|---|
SEC00-J |
medium |
likely |
high |
P6 |
L2 |
Automated Detection
Identifying sensitive information requires assistance from the programmer; fully-automated identification of sensitive information is beyond the current state of the art.
If we had user-provided tagging of sensitive information, we could do some kind of escape analysis on the doPrivileged()
blocks and perhaps prove that nothing sensitive leaks out of them. We could even use something akin to thread coloring to identify the methods that either must (or must not) be called from doPrivileged()
blocks.
Related Guidelines
CWE ID 266, "Incorrect Privilege Assignment" |
|
|
CWE ID 272, "Least Privilege Violation" |
Secure Coding Guidelines for the Java Programming Language, Version 3.0 |
Guideline 6-2 Safely invoke java.security.AccessController.doPrivileged() |
Bibliography
<ac:structured-macro ac:name="unmigrated-wiki-markup" ac:schema-version="1" ac:macro-id="2293916c-d699-43e9-86f2-2ce9ac5d15ea"><ac:plain-text-body><![CDATA[ |
[[API 2006 |
AA. Bibliography#API 06]] |
[method doPrivileged() |
http://java.sun.com/javase/6/docs/api/java/security/AccessController.html#doPrivileged(java.security.PrivilegedAction)] |
]]></ac:plain-text-body></ac:structured-macro> |
<ac:structured-macro ac:name="unmigrated-wiki-markup" ac:schema-version="1" ac:macro-id="6c0e41df-8f79-4703-b507-03ce9d250ed6"><ac:plain-text-body><![CDATA[ |
[[Gong 2003 |
AA. Bibliography#Gong 03]] |
Sections 6.4, AccessController and 9.5 Privileged Code |
]]></ac:plain-text-body></ac:structured-macro> |