Every program operates in several security domains. Any system can be divided into several subsystem component, where each component has a specific security domain. For instance, one component may have access to the filesystem, but not the network, while another component can access the network but not the filesystem. When two components share data, if they have different degrees of trust, the data is said to flow across a trust boundary.
Since Java allows untrusted code to run alongside trusted code, it is perfectly possible for a Java program to maintain different security domains, and to have internal trust boundaries. Every Java program contains both locally-written code (that is, local to the developers) as well as code whose implementation is beyond the developers' control. Similarly, even within code whose implementation is controlled by the developers, there may be a distinction between trusted and untrusted code. Alternatively, the locally developed portions of a program may include both application-specific code and also locally-developed library code that is shared with other programs (either local or external). We define a trust boundary to be the points at which control or data pass from one aggregation of locally-developed code to another aggregation of code, without regard to whether the second aggregation is locally or externally developed.
The trust boundaries of any system are mandated by the system administrator. While the system components can provide support for trust boundaries, they cannot dictate what trust is given to any component. Consequently, the deployer of a system must define the trust boundaries as part or the system's security policy. Any security auditor can then take that definition and ensure that the software adequately supports the policy. The definition of security domains — and consequently of the boundaries between those domains — is necessarily application-specific.
We recommend that any library that may be exported outside its home organization should consider itself to be in its own unique security domain (in the sense that its client code cannot be known when the library is written). Consequently the library should consider its API to be a trust boundary. Similarly, we recommend that libraries acquired from outside organizations should be considered to be separate security domains from in-house code. Circumstances will differ; the important thing is to identify and respect the necessary security domains in your application.
Any program that maintains a trust boundary must deal with data coming in over that trust boundary...that is, from a process with a differing security domain. Likewise, any program with must also deal with data going out over a trust boundary. It is imperative that data that crosses a trust boundary undergo filtering. We shall examine the two cases in turn:
Data Output
Data that is output by a program may be sent to some component of the system with a different security domain. In this case, the program must ensure that the data is suitable to the remote component's trust boundary. The system must do this by eamining the data and filtering out any sensitive information.
Like trust boundaries, the question of what information is /sensitive/ is resolved by the system's security policy. Therefore, a program cannot define which information is sensitive, it can only provide support for handling information that may potentially be declared sensitive by the system administrator.
For instance, if malicious code manages to infiltrate a system, many attacks will be futile if the system's output is appropriately escaped and encoded. Refer to the guideline IDS04-J. Properly encode or escape output for more details.
Java programs have many opportunities to output sensitive information. Several rules address the mitigation of sensitive information disclosure, including EXC06-J. Do not allow exceptions to expose sensitive information and FIO08-J. Do not log sensitive information.
Data Input
Data that is received by a program from a source outside the program's trust boundary may, in fact, be malicious. The program must therefore take steps to ensure the data is valid and not malicious.
These steps can include the following:
/Validation/, in the broadest sense, is the process of ensuring that input data falls within the expected range of valid program input. For example, method arguments must conform not only to the type and numeric range requirements of a method or subsystem, but also contain data that conforms to the required input invariants for that method.
/Sanitization/: In many cases, the data may be fed directly to some subsystem. Data sanitization is the process of ensuring that data conforms to the requirements of the subsystem to which it are passed. Sanitization also involves ensuring that data also conforms to any security-related requirements regarding leaking or exposure of sensitive data when it is output across a trust boundary. Refer to the related guideline IDS01-J. Carefully filter any data that passes through a trust boundary for more details on data sanitization. Data sanitization and input validation may coexist and complement each other.
/Canonicalization/ and /Normalization/: Canonicalization is the process of lossless reduction of the input to its equivalent simplest known form. Normalization is the process of lossy conversion of the data to its simplest known (and anticipated) form. Canonicalization and normalization must occur before validation to prevent attackers from exploiting the validation routine to strip away illegal characters and thus construct a forbidden character sequence. Refer to the guideline IDS02-J. Validate strings after performing normalization for more details. In addition, ensure that normalization is performed only on fully assembled user input. Never normalize partial input or combine normalized input with non-normalized input.
For instance, POSIX filesystems provide a syntax for expressing file names on the system using paths. A path is a string which indicates how to find any file by starting at a particular directory (usually the current working directory), and traversing down directories until the file is found. A path is canonical if it contains no symbolic links, and no special entries, such as '.' or '..'; as these are handled specially on POSIX systems. Every file accessible from a directory has exactly one canonical path, along with many non-canonical paths.
Many rules address proper filtering of untrusted input, especially when such input is passed to a complex subsystem. For example, see IDS08-J. Prevent XML Injection.
Risk Assessment
Failure to properly filter data that crosses a trust boundary can cause information leakage and execution of arbitrary code.
Guideline |
Severity |
Likelihood |
Remediation Cost |
Priority |
Level |
---|---|---|---|---|---|
IDS00-J |
high |
probable |
medium |
P12 |
L1 |
Related Vulnerabilities
Bibliography
[[OWASP 2005]]
[[OWASP 2007]]
13. Input Validation and Data Sanitization (IDS) 13. Input Validation and Data Sanitization (IDS) IDS01-J. Carefully filter any data that passes through a trust boundary