...
Code Block | ||
---|---|---|
| ||
public class Password { public static void changePassword(final String password_file) throws FileNotFoundException { FileInputStream fin; fin = openPasswordFile(password_file); } public static FileInputStream openPasswordFile(String password_file) throws FileNotFoundException { // Declare as final and assign before the body of the anonymous inner class // Array f[] is used to maintain language semantics while using final final FileInputStream f[] = {null}; // Use own privilege to open the sensitive password file AccessController.doPrivileged(new PrivilegedAction() { public Object run() { try { f[0] = new FileInputStream(password_file); //Perform privileged action } catch(FileNotFoundException cnf) { // cannot recover if password file is not found; log to file } return null; //Still mandatory to return from run() } }); return f[0]; //Returns a reference to privileged objects (inappropriate) } } |
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. Both compliant solutions (below) avoid exposing any reference to the privileged data.
Compliant Solution (Logging Exceptions)
The openPasswordFile()
method controls access to the sensitive password file and returns its reference. For this reason, it should not be directly invokable. Instead, the changePassword()
method must be used to forward any requests to openPasswordFile()
. This is because changePassword()
does not return a reference to the sensitive file to any caller and processes the file internally. Observe that caller supplied (tainted) inputs are not used because the name of the password file is hard-coded.
This compliant solution logs exceptions to a privileged log file. This choice is appropriate when data attached to the exception could potentially leak privileged information (e.g. the path to a privileged file).
Code Block | ||
---|---|---|
| ||
class Password { private static void changePassword() { // Use own privilege to open the sensitive password file final String password_file = "password"; final FileInputStream f[] = {null}; AccessController.doPrivileged(new PrivilegedAction() { public Object run() { try { f[0] = openPasswordFile(password_file); // call the privileged method here } catch(FileNotFoundException cnf) { // cannot recover if password file is not found; log to file } return null; } }); //Perform other operations such as old password verification } private static FileInputStream openPasswordFile(String password_file) throws FileNotFoundException { FileInputStream f = new FileInputStream(password_file); // Perform read/write operations on password file return f; } } |
Compliant Solution (Throwing Wrapped Exceptions)
The previous compliant solution logs the exception instead of revealing sensitive information. (See guideline EXC06-J. Do not allow exceptions to transmit sensitive information for more information.) Sometimes no sensitive information can be revealed by any When none of the possible exceptions . In such cases, reveals sensitive information, we can use an equivalent mechanism that allows exceptions to be wrapped can be used. This allows the caller to obtain thus providing better diagnostic information for the caller. For example, if an applet doesn't have that lacks read-access to read system files that contain fonts , it can accomplish the task from a privileged block without revealing any sensitive information. In fact, if When non-sensitive exceptions provide more information, the client can deduce is better able to recognize the symptoms of a read failure more easily.
Code Block | ||
---|---|---|
| ||
public static void readFont() throws FileNotFoundException { // Use own privilege to open the font file final String font_file = "fontfile"; try { final InputStream in = AccessController.doPrivileged(new PrivilegedExceptionAction<InputStream>() { public InputStream run() throws FileNotFoundException { return openFontFile(font_file); //call the privileged method here } }); // Perform other operations } catch (PrivilegedActionException exc) { Exception cause = exc.getException(); if (cause instanceof FileNotFoundException) { throw (FileNotFoundException)cause; } else { throw new Error("Unexpected exception type", cause); } } } |
In summary, if the code can throw a checked exception without leaking sensitive information, prefer the form of doPrivileged
method that takes a PrivilegedExceptionAction
instead of a PrivilegedAction
.
...