ERR03-G: Do Not Throw Exceptions That Expose Sensitive Information
Printing a stack trace from an exception can inadvertently expose details about an application's inner workings, including its process, structure, and state. This is great for debugging purposes, but attackers frequently exploit this feature. For example, a FileNotFoundException message could inadvertently divulge insights into the file system layout, while the exception type might hint at the absence of a requested file.
This rule applies universally, encompassing both server-side and client-side applications. For example, vulnerable web servers and browsers can unwittingly leak sensitive data to attackers. In a notable 2004 incident, Schönefeld unearthed an exploit in Opera v7.54, allowing attackers to exploit a Java class in an applet to gather information about the logged-in user and their home directory.
All exceptions risk divulging information that could be exploited for denial-of-service (DoS) attacks against the system. Hence, it is crucial for programs to meticulously filter both exception messages and types that might traverse trust boundaries. The following table delineates several problematic exceptions and the corresponding information leaks or threats they pose:
Exception Name | Description of Information Leak or Threat |
---|---|
java.io.FileNotFoundException | Underlying file system structure, username enumeration |
java.sql.SQLException | Database structure, username enumeration |
java.net.BindException | Enumeration of open ports where an untrusted client can choose a server port |
java.util.ConcurrentModificationException | May provide information about thread-unsafe code |
javax.naming.InsufficientResourcesException | Insufficient server resources (may aid in DoS) |
java.util.MissingResourceException | Resource enumeration |
java.util.jar.JarException | Underlying file system structure |
java.security.acl.NotOwnerException | Owner enumeration |
java.lang.OutOfMemoryError | DoS |
java.lang.StackOverflowError | DoS |
Non-Compliant Code Example (Leaks from Exception Message and Type)
In this non-compliant code example, the program must read a file supplied by the user, but the contents and layout of the file system are sensitive. The program accepts a file name as an input argument but fails to prevent any resulting exceptions from being presented to the user.
// Non-compliant Code Example
public static function readFile(filename : String) : void {
// Linux stores a user's home directory path in
// the environment variable $HOME, Windows in %APPDATA%
using (var fis = new FileInputStream(System.getenv("APPDATA")
+ "\\" + filename)) {
// read file
}
}
When a requested file is absent, the FileInputStream
constructor throws a FileNotFoundException
, leaking information about the file system:
java.io.FileNotFoundException: D:\Users\rseacord\AppData\Roaming\nonexistent_file (The system cannot find the file specified)
at java.base/java.io.FileInputStream.open0(Native Method)
at java.base/java.io.FileInputStream.open(FileInputStream.java:219)
at java.base/java.io.FileInputStream.<init>(FileInputStream.java:157)
at java.base/java.io.FileInputStream.<init>(FileInputStream.java:112)
This information allows an attacker to reconstruct the underlying file system by repeatedly passing fictitious path names to the program.
Non-Compliant Code Example (Translate Exception)
This non-compliant code example logs the exception and then wraps it in a more general exception before rethrowing it:
// Non-compliant Code Example
public static function readFileWrap(filename : String) : void {
// Linux stores a user's home directory path in
// the environment variable $HOME, Windows in %APPDATA%
try {
var myfis = new FileInputStream(System.getenv("APPDATA") + "\\" + filename)
// read file
} catch (fnfe: FileNotFoundException) {
// Log the exception
throw new IOException("Unable to retrieve file", fnfe);
}
}
Even when the logged exception is inaccessible to the user, the original exception is still informative. For example, an attacker can use it to discover sensitive information about the file system layout.
java.io.IOException: Unable to retrieve file
at _nopackage_.GosuScratchpad.readFileWrap(Gosu Scratchpad.gsp:23)
at _nopackage_.GosuScratchpad.evaluate(Gosu Scratchpad.gsp:30)
at gw.lang.Gosu.runWithFile(Gosu.java:670)
at gw.lang.Gosu.start(Gosu.java:149)
at gw.lang.Gosu.main(Gosu.java:79)
Caused by: java.io.FileNotFoundException: D:\Users\rseacord\AppData\Roaming\nonexistent_file (The system cannot find the file specified)
at java.base/java.io.FileInputStream.open0(Native Method)
Non-Compliant Code Example (Sanitized Exception)
This non-compliant code example logs the exception and throws a custom exception that does not wrap the FileNotFoundException:
// Non-compliant Code Example
class SecurityIOException extends IOException {}
public static function readFileSanitized(filename : String) : void {
// Linux stores a user's home directory path in
// the environment variable $HOME, Windows in %APPDATA%
try {
var myfis = new FileInputStream(System.getenv("APPDATA") + "\\" +
filename)
// read file
} catch (fnfe: FileNotFoundException) {
// Log the exception
throw new SecurityIOException();
}
}
Although this exception is less likely to leak useful information when compared to the previous non-compliant code examples, it still reveals that the specified file cannot be read:
SecurityIOException
at _nopackage_.GosuScratchpad.readFileSanitized(Gosu Scratchpad.gsp:35)
at _nopackage_.GosuScratchpad.evaluate(Gosu Scratchpad.gsp:42)
at gw.lang.Gosu.runWithFile(Gosu.java:670)
at gw.lang.Gosu.start(Gosu.java:149)
at gw.lang.Gosu.main(Gosu.java:79)
More specifically, the program reacts differently to nonexistent file paths versus valid ones. This behaviour is an oracle that an attacker can use to infer sensitive information about the file system, asking a series of yes/no questions. Failure to restrict user input leaves the system vulnerable to a brute-force attack. The attacker discovers valid file names by issuing queries covering the space of possible file names. File names that cause the program to throw SecurityIOException
indicate nonexistent files, whereas file names that do not return exceptions reveal existing files.
Compliant Solution
This compliant solution issues a terse error message when the file cannot be opened. Information about the file system is concealed. The usability of this solution is degraded to improve security, but an authorized user should be able to access the logs to determine the true cause of the error.
// Compliant Solution
public static function readFileCS(filename : String) : void {
var file : File = null
try {
file = new File(System.getenv("APPDATA") + filename)
}
catch (x : IOException) {
print("Invalid file")
return
}
try {
var fis = new FileInputStream(file)
}
catch (x : FileNotFoundException) {
print("Invalid file")
return
}
}
Risk Assessment
Exceptions may inadvertently reveal sensitive information unless care is taken to limit the information disclosure.
Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
---|---|---|---|---|---|
ERR03-G | Medium | Likely | High | L4 | L3 |
Related Vulnerabilities
CVE-2009-2897 describes cross-site scripting (XSS) vulnerabilities in several versions of SpringSource Hyperic HQ. These vulnerabilities allow remote attackers to inject arbitrary web scripts or HTML via invalid values for numerical parameters. An uncaught java demonstrates the java.lang.NumberFormatException exception results from entering several invalid numeric parameters into the web interface.
CVE-2015-2080 describes a vulnerability in the Jetty web server, versions 9.2.3 to 9.2.8, where an illegal character passed in an HTML request causes the server to respond with an error message containing the text with the illegal character. However, this error message can also contain sensitive information, such as cookies from previous web requests.
Additional Resources
Was this page helpful?