Both environment variables and system properties provide user-defined mappings between keys and their corresponding values and can be used to communicate those values from the environment to a process. According to the Java API [API 2014] java.lang.System
class documentation:
Environment variables have a more global effect because they are visible to all descendants of the process which defines them, not just the immediate Java subprocess. They can have subtly different semantics, such as case insensitivity, on different operating systems. For these reasons, environment variables are more likely to have unintended side effects. It is best to use system properties where possible. Environment variables should be used when a global effect is desired, or when an external system interface requires an environment variable (such as
PATH
).
Programs that execute in a more trusted domain than their environment must assume that the values of environment variables are untrusted and must sanitize and validate any environment variable values before use.
The default values of system properties are set by the Java Virtual Machine (JVM) upon startup and can be considered trusted. However, they may be overridden by properties from untrusted sources, such as a configuration file. System properties from untrusted sources must be sanitized and validated before use.
The Java Tutorial [Campione 1996] states:
To maximize portability, never refer to an environment variable when the same value is available in a system property. For example, if the operating system provides a user name, it will always be available in the system property
user.name
.
Actually, relying on environment variables is more than a portability issue. An attacker can essentially control all environment variables that enter a program using a mechanism such as the java.lang.ProcessBuilder
class.
Consequently, when an environment variable contains information that is available by other means, including system properties, that environment variable must not be used. Finally, environment variables must not be used without appropriate validation.
Noncompliant Code Example
This noncompliant code example tries to get the user name, using an environment variable:
Code Block | ||
---|---|---|
| ||
String username = System.getenv("USER");
|
First, this is a portability issue. The Java Tutorial Campione 1996 further suggests:
The way environment variables are used also varies. For example, Windows provides the user name in an environment variable called
USERNAME
, while UNIX implementations might provide the user name inUSER
,LOGNAME
, or both.
Second, an attacker can execute this program with the USER
environment variable set to any value he or she chooses. The following code example does just that on a POSIX platform:
Code Block | ||
---|---|---|
| ||
public static void main(String args[]) {
if (args.length != 1) {
System.err.println("Please supply a user name as the argument |
The getenv()
function searches an environment list for a string that matches a specified name and returns a pointer to a string associated with the matched list member.
Wiki Markup |
---|
Section 7.20.4.5 of C99 states that \[[ISO/IEC 9899:1999|AA. Bibliography#ISO/IEC 9899-1999]\] |
The set of environment names and the method for altering the environment list are implementation-defined.
Depending on the implementation, multiple environment variables with the same name may be allowed and can cause unexpected results if a program cannot consistently choose the same value. The GNU glibc library addresses this issue in getenv()
and setenv()
by always using the first variable it encounters and ignoring the rest. However, it is unwise to rely on this.
Wiki Markup |
---|
One common difference between implementations is whether or not environment variables are case sensitive. While UNIX-like implementations are generally case sensitive, environment variables are "not case sensitive in Windows 98/Me and Windows NT/2000/XP" \[[MSDN|AA. Bibliography#MSDN]\]. |
Duplicate Environment Variable Detection (POSIX)
The following code defines a function that uses the POSIX environ
array to manually search for duplicate key entries. Any duplicate environment variables are considered an attack, so the program immediately terminates if a duplicate is detected.
Code Block | ||
---|---|---|
| ||
extern char **environ; int main(void) { if (multiple_vars_with_same_name()) { printf("Someone may be tampering.\n"); return 1; } /* ... */ return 0; } int multiple_vars_with_same_name(void) { size_t i; size_t j; size_t k; size_t l; size_t len_i; size_t len_j; for(size_t i = 0; environ[i] != NULL; i++) String user = args[0]; ProcessBuilder pb = new ProcessBuilder(); pb.command("/usr/bin/printenv"); Map<String,String> environment = pb.environment(); environment.put("USER", user); pb.redirectErrorStream(true); try { for(size_t j = i; environ[j] != NULL; j++) { if (i != j) { k = 0Process process = pb.start(); InputStream lin = 0; len_i = strlen(environ[i]process.getInputStream(); len_j = strlen(environ[j]); int c; while ((kc < len_i && l < len_j) { if (environ[i][k] != environ[j][l]) break; = in.read()) != -1) { if (environ[i][k] == '=') return 1; System.out.print((char) c); k++;} int exitVal l++= process.waitFor(); } catch (IOException x) }{ // Forward } }to handler } return 0; } |
Noncompliant Code Example
The following noncompliant code behaves differently when compiled and run on Linux and Microsoft Windows platforms.
Code Block | ||
---|---|---|
| ||
if (putenv("TEST_ENV=foo") != 0 catch (InterruptedException x) { /* Handle error *// } if (putenv("Test_ENV=bar") != 0) { /* Handle error */ } const char *temp = getenv("TEST_ENV"); if (temp == NULL) { /* Handle error */ } printf("%s\n", temp); |
On an IA-32 Linux machine with GCC Compiler Version 3.4.4, this code prints
Code Block |
---|
foo
|
whereas, on an IA-32 Windows XP machine with Microsoft Visual C++ 2008 Express, it prints
Code Block |
---|
bar
|
Compliant Solution
Forward to handler
}
}
|
This program runs the POSIX /usr/bin/printenv
command, which prints out all environment variables and their values. It takes a single argument string and sets the USER
environment variable to that string. The subsequent output of the printenv
program will indicate that the USER
environment variable is set to the string requested.
Compliant Solution
This compliant solution obtains the user name using the user.name
system property. The Java Virtual Machine (JVM), upon initialization, sets this system property to the correct user name, even when the USER
environment variable has been set to an incorrect value or is missingPortable code should use environment variables that differ by more than capitalization.
Code Block | ||
---|---|---|
| ||
if (putenv("TEST_ENV=foo") != 0) { /* Handle error */ } if (putenv("OTHER_ENV=bar") != 0) { /* Handle error */ } const char *temp = getenv("TEST_ENV"); if (temp == NULL) { /* Handle error */ } printf("%s\n", tempString username = System.getProperty("user.name"); |
Risk Assessment
An attacker can create multiple Untrusted environment variables with (for example, by using the POSIX execve()
function). If the program checks one copy but uses another, security checks may be circumvented.can provide data for injection and other attacks if not properly sanitized.
Rule |
---|
Severity | Likelihood | Remediation Cost | Priority | Level | |
---|---|---|---|---|---|
ENV02-J | Low |
Likely |
Low |
P9 |
L2 |
Automated Detection
Related Vulnerabilities
Search for vulnerabilities resulting from the violation of this rule on the CERT website.
Related Guidelines
ENV02-C. Beware of multiple environment variables with the same effective name | |
ENV02-CPP. Beware of multiple environment variables with the same effective name | |
Section 7.20.4, "Communication with the Environment" | |
"XYS Executing or Loading Untrusted Code" | |
CWE-462, "Duplicate Key in Associative List (Alist)" | |
CWE-807, "Reliance on Untrusted Inputs in a Security Decision" |
Bibliography
<ac:structured-macro ac:name="unmigrated-wiki-markup" ac:schema-version="1" ac:macro-id="801fb420-be26-43c2-b49c-17b9fddbbcf8"><ac:plain-text-body><![CDATA[ | [[MSDN | AA. Bibliography#MSDN]] | [ | http://msdn.microsoft.com/en-us/library/tehxacec(VS.71).aspx] | ]]></ac:plain-text-body></ac:structured-macro> |
Tool | Version | Checker | Description | ||||||
---|---|---|---|---|---|---|---|---|---|
Parasoft Jtest |
| CERT.ENV02.ENV | Do not use the non-portable 'System.getenv()' method | ||||||
PVS-Studio |
| V6110 |
Android Implementation Details
On Android, the environment variable user.name
is not used and is left blank. However, environment variables exist and are used on Android, so the rule is applicable.
Bibliography
...
void ENV05-J. Do not grant RuntimePermission with target createClassLoader 15. Runtime Environment (ENV) ENV07-J. Do not deploy an application that can be accessed using the Java Platform Debugger Architecture