Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

Code Block
bgColor#ffcccc
String file = /* Provided by user */;
InputStream in = null;
try {
  in = new FileInputStream(file);
  // ...
} finally {
  try {
    if (in !=null) { in.close();}
  } catch (IOException x) {
    // Handle error
  }
}

Noncompliant Code Example

...

This noncompliant code example uses the try-with-resources statement from (introduced in Java SE 7) to open the file. The try-with-resources statement guarantees the file's successful closure if an exception is thrown, but this code is subject to the same vulnerabilities as the previous example.

Code Block
bgColor#ffcccc
String filename = /* Provided by user */;
Path path = new File(filename).toPath();
try (InputStream in = Files.newInputStream(path)) {
  // Read file
} catch (IOException x) {
  // Handle error
}

Noncompliant Code Example (

...

isRegularFile())

This noncompliant code example first checks that the file is a regular file (using the NIO.2 API) before opening it:

...

This test can still be circumvented by a symbolic link. By default, the readAttributes() method follows symbolic links and reads the file attributes of the final target of the link. The result is that the program may reference a file other than the one intended.

Noncompliant Code Example (

...

NOFOLLOW_LINKS)

This noncompliant code example checks the file by calling the readAttributes() method with the NOFOLLOW_LINKS link option to prevent the method from following symbolic links. This approach allows the detection of symbolic links because the isRegularFile() check is carried out on the symbolic link file and not on the final target of the link.

...

This code is still vulnerable to a time-of-check, time-of-use (TOCTOU) race condition. For example, an attacker can replace the regular file with a file link or device file after the code has completed its checks but before it opens the file.

Noncompliant Code Example (POSIX

...

: Check-Use-Check)

This noncompliant code example performs the necessary checks and then opens the file. After opening the file, it performs a second check to make sure that the file has not been moved and that the file opened is the same file that was checked. This approach reduces the chance that an attacker has changed the file between checking and then opening the file. In both checks, the file's fileKey attribute is examined. The fileKey attribute serves as a unique key for identifying files and is more reliable than the path name as on indicator of the file's identity.

...

  • The TOCTOU race condition still exists between the first check and open. During this race window, an attacker can replace the regular file with a symbolic link or other nonregular file. The second check detects this race condition but does not eliminate it.
  • An attacker could subvert this code by letting the check operate on a regular file, substituting the nonregular file for the open, and then resubstituting the regular file to circumvent the second check. This vulnerability exists because Java lacks a mechanism to obtain file attributes from a file by any means other than the file name, and the binding of the file name to a file object is reasserted every time the file name is used in an operation. Consequently, an attacker can still swap a file for a nefarious file, such as a symbolic link.
  • A system with hard links allows an attacker to construct a malicious file that is a hard link to a protected file. Hard links cannot be reliably detected by a program and can foil canonicalization attempts, which are prescribed by FIO16-J. Canonicalize path names before validating them.

Compliant Solution (POSIX

...

: Secure Directory)

Because of the potential for race conditions and the inherent accessibility of shared directories, files must be operated on only in secure directories. Because programs may run with reduced privileges and lack the facilities to construct a secure directory, a program may need to throw an exception if it determines that a given path name is not in a secure directory.

...

Code Block
bgColor#ccccff
public static boolean isInSecureDir(Path file) {
  return isInSecureDir(file, null);
}
public static boolean isInSecureDir(Path file, UserPrincipal user) {
   return isInSecureDir(file, user, 5);
}

/**
 * Indicates whether file lives in a secure directory relative
 * to the program's user
 * @param file Path to test
 * @param user User to test. If null, defaults to current user
 * @param symlinkDepth Number of symbolic links allowed
 * @return true if file's directory is secure.
 */
public static boolean isInSecureDir(Path file, UserPrincipal user,
                                    int symlinkDepth) {
  if (!file.isAbsolute()) {
    file = file.toAbsolutePath();
  } if (symlinkDepth <=0) {
      // Too many levels of symbolic links
      return false;
    }

  // Get UserPrincipal for specified user and superuser
  FileSystem fileSystem =
      Paths.get(file.getRoot().toString()).getFileSystem();
  UserPrincipalLookupService upls = 
      fileSystem.getUserPrincipalLookupService();
  UserPrincipal root = null;
  try {
    root = upls.lookupPrincipalByName("root");
    if (user == null) {
      user = upls.lookupPrincipalByName(System.getProperty("user.name"));
    }
    if (root == null || user == null) {
      return false;
    }
  } catch (IOException x) {
    return false;
  }

  // If any parent dirs (from root on down) are not secure,
  // dir is not secure
  for (int i = 1; i <= file.getNameCount(); i++) {
    Path partialPath = Paths.get(file.getRoot().toString(),
                                 file.subpath(0, i).toString());

    try {
      if (Files.isSymbolicLink(partialPath)) {
        if (!isInSecureDir(Files.readSymbolicLink(partialPath),)) {
                           user, symlinkDepth - 1)) {
          // Symbolic link, linked-to dir not secure
          return false;
        }
      } else {
        UserPrincipal owner = Files.getOwner(partialPath);
        if (!user.equals(owner) && !root.equals(owner)) {
          // dir owned by someone else, not secure
          return false;
        }
        PosixFileAttributes attr =
            Files.readAttributes(partialPath, PosixFileAttributes.class);
        Set<PosixFilePermission> perms = attr.permissions();
        if (perms.contains(PosixFilePermission.GROUP_WRITE) ||
            perms.contains(PosixFilePermission.OTHERS_WRITE)) {
          // Someone else can write files, not secure
          return false;
        }
      }
    } catch (IOException x) {
      return false;
    }
  }

  return true;
}

...

Programs with elevated privileges may need to write files to directories owned by unprivileged users. One example is a mail daemon that reads a mail message from one user and places it in a directory owned by another user. In such cases, the mail daemon should assume the privileges of a user when reading or writing files on behalf of that user, in which case all file access should occur in secure directories relative to that user. When a program with elevated privileges must write files on its own behalf, these files should be in secure directories relative to the privileges of the program (such as directories accessible only by the system administrator).

Exceptions

FIO00-J-EX0: Programs that operate on single-user systems or on systems that have no shared directories or no possibility of file system vulnerabilities do not need to ensure that files are maintained in secure directories before operating on them.

...

Performing operations on files in shared directories can result in DoS attacks. If the program has elevated privileges, privilege escalation exploits are possible.

Rule

Severity

Likelihood

Remediation Cost

Priority

Level

FIO00-J

Medium

Unlikely

Medium

P4

L3

Related Guidelines

Android Implementation Details

On Android, the SD card ( /sdcard or /mnt/sdcard ) is shared among multiple applications, so sensitive files should not be stored on the SD card (see DRD00-J. Do not store sensitive information on external storage (SD card)).

Bibliography

[API 2014]

Class FileLock
   Methods createTempFile
   Method delete
   Method deleteOnExit

[Darwin 2004]

Section 11.5, "Creating a Transient File"

[Garfinkel 1996]

Section 5.6, "Device Files"

[Howard 2002]

Chapter 11, "Canonical Representation Issues"

[J2SE 2011]

"The try-with-resources Statement"

[JDK Bug 2015]

Bug

ID 4405521

JDK-4171239
Bug JDK-4405521 
Bug

ID

JDK-4631820

[Open Group 2004]

open()

[SDN 2008]

Bug ID 4171239

[Secunia 2008]

Secunia Advisory 20132

...


...