...
The process of canonicalizing file names makes it easier to verify an alias. More than one alias can refer to a single directory or file. Further, the textual representation of an alias may yield little or no information regarding the directory or file to which it refers. Consequently, all aliases should must be fully resolved or canonicalized before validation. In the absence of a security manager, aliases must be canonicalized before validation. This is necessary because untrusted user input may allow an input-output I/O operation to escape the specified operating directory. Violation of this guideline can result in information disclosure and malicious modification of files existing in directories other than the specified one.
This guideline is an instance of IDS02-J. Normalize strings before validating them.
Noncompliant Code Example
...
Note that File.getAbsolutePath()
resolves actaully does resolve all symbolic links, aliases and short cuts on all known Windows and Macintosh platforms. Nevertheless, the JLS lacks any guarantee either that this behavior is present on all platforms or that it will continue in future implementations.
Code Block | ||
---|---|---|
| ||
public static void main(String[] args) {
File f = new File("/tmp/" + args[0]);
String absPath = f.getAbsolutePath();
if (!validate(absPath)) { // Validation
throw new IllegalArgumentException();
}
}
|
Compliant Solution (getCanonicalPath()
)
This compliant solution uses the getCanonicalPath()
method, introduced in Java 2, because it resolves all aliases, shortcuts or symbolic links consistently, across all platforms. The value of the alias (if any) is not included in the returned value. Moreover, relative references like the double period (..
) are also removed so that the input is reduced to a canonicalized form before validation is carried out. An adversary cannot use ../
sequences to break out of the specified directory when the validate()
method is present.
Code Block | ||
---|---|---|
| ||
public static void main(String[] args) throws IOException {
File f = new File("/tmp/" + args[0]);
String canonicalPath = f.getCanonicalPath();
if (!validate(canonicalPath)) { // Validation
throw new IllegalArgumentException();
}
}
|
The getCanonicalPath()
method throws a security exception when used within applets as it reveals too much information about the host machine. The getCanonicalFile()
method behaves like getCanonicalPath()
but returns a new File
object instead of a String
.
Compliant
...
Solution (Security Manager)
A comprehensive way of handling this issue is to grant the application the permissions to operate only on files present within the intended directory — /tmp
in this example. This compliant solution specifies the absolute path of the program in its security policy file, and grants java.io.FilePermission
with target /tmp
and read
and write
.
...
This noncompliant code example attempts to mitigate the issue by using the File.getCanonicalPath()
method, which fully resolves the argument and constructs a canonicalized path. For example, the path /img/../etc/passwd
resolves to /etc/passwd
. Canonicalization Validation without validation canonicalization remains insecure because the user remains able to specify files outside the intended directory.
...
Code Block | ||
---|---|---|
| ||
File f = new File("/img/" + args[0]); String canonicalPath = f.getCanonicalPath(); if (canonicalPath.equals("/img/java/file1.txt")) { // Validation // Do something } if (!canonicalPath.equals("/img/java/file2.txt")) { // Validation // Do something } FileOutputStream fis = new FileOutputStream(f); |
Compliant solution (Security Manager)
A comprehensive solution is to grant the application the permissions to read only the specifically intended files or directories. Grant these permissions by to specifying the absolute path of the program in the security policy file and granting java.io.FilePermission
with the canonicalized absolute path of the file or directory as the target name and with the action set to read
.
...