Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Parasoft Jtest 2021.1

Temporary files are typically can be used to

  • Share data between processes.
  • Store auxiliary program data (for example, to save preserve memory).
  • Construct and/or load classes, JAR files, and native libraries dynamically.

Programmers frequently create temporary files in directories that are writable by everyone; examples include /tmp and /var/tmp on UNIX and C:\TEMP on Windows. Files in such directories may be purged regularly, for example, every night or during reboot. However, an adversary who has access to the local file system can misuse files held in world-writable directories when those files are created insecurely or remain accessible after use. For instance, an attacker who can both predict the name of a temporary file and can change or replace that file, can exploit a time-of-check time-of-use (TOCTOU) condition to cause either a failure in creating the temporary file from within program code or reading of file whose contents are determined by the attacker.

Mitigation strategies include:

1. Use of other IPC mechanisms such as sockets and remote procedure calls
2. Use of the low-level Java Native Interface (JNI).
3. Use of memory mapped files
4. Use of threads to share heap data within the same JVM (applies to data sharing between Java processes only)
5. Use of a secure directory that can be accessed only by application instances. When using this strategy, ensure that multiple instances of the application running on the same platform avoid competing for the same files.

Shared access to a directory entails greater vulnerability than does shared access to a limited number of files. Consequently, temporary files in shared directories must be

1. Created with unique and unpredictable file names,
2. Opened with exclusive access,
3. Removed before the program exits, and
4. Opened with appropriate permissions.

Secure creation of temporary files is error prone and relies on platform dependent behavior; the Operating System, and the file system are the determining factors. Code that works for a locally mounted file system, for example, may be vulnerable when used with a remotely mounted file system. Moreover, most relevant APIs are problematic. The only secure comprehensive solution is to refrain from creating temporary files in shared directories.

Unique and Unpredictable Filenames

Wiki Markup
A recently identified bug in JRE and JDK version 6.0 and earlier permits an attacker who can predict the names of temporary files to write malicious JAR files via unknown vectors \[[CVE 2008|AA. Bibliography#CVE 08]\]. Failure to reclaim temporary resources can cause rapid disk space exhaustion due to unreclaimed files \[[Secunia Advisory 20132|http://secunia.com/advisories/20132/]\].

Exclusive Access

Wiki Markup
Exclusive access grants unrestricted file access to the locking process while denying access to all other processes, thus eliminating the potential for a race condition on the locked region. The {{java.nio.channels.FileLock}} class facilitates file locking. According to the Java API \[[API 2006|AA. Bibliography#API 06]\] documentation

A file lock is either exclusive or shared. A shared lock prevents other concurrently-running programs from acquiring an overlapping exclusive lock, but does allow them to acquire overlapping shared locks. An exclusive lock prevents other programs from acquiring an overlapping lock of either type. Once it is released, a lock has no further effect on the locks that may be acquired by other programs.

Shared locks support concurrent read access from multiple processes; exclusive locks support exclusive write access. File locks provide protection across processes; they are ineffective for multiple threads within a single process. Both shared locks and exclusive locks eliminate the potential for a cross-process race condition on the locked region. Exclusive locks provide mutual exclusion; shared locks prevent alteration of the state of the locked file region (one of the required properties for a data race).

Wiki Markup
"Whether or not a lock actually prevents another program from accessing the content of the locked region is system-dependent and consequently unspecified" \[[API 2006|AA. Bibliography#API 06]\].

Microsoft Windows uses a file-locking mechanism called mandatory locking because every process attempting access to a locked file region is subject to the restriction.

Linux implements both mandatory locks and advisory locks. Advisory locks are not enforced by the operating system, which diminishes their value from a security perspective. Unfortunately, the mandatory file lock in Linux is generally impractical because:

  • Mandatory locking is supported only on local file systems; it lacks support for network file systems (such as NFS or AFS).
  • File systems must be explicitly mounted with support for mandatory locking; this support is disabled by default.
  • Locking relies on the set-group-ID bit, however that bit can be disabled by another process (thereby defeating the lock).
Removal Before Termination

Removing temporary files when they are no longer required allows file names and other resources (such as secondary storage) to be recycled. Each program is responsible for ensuring that temporary files are removed during normal operation. There is no surefire method that can guarantee the removal of orphaned files in the case of abnormal termination, even in the presence of a finally block, because the finally block may fail to execute. For this reason, many systems employ temporary file cleaner utilities to sweep temporary directories and remove old files. Such utilities can be invoked manually by a system administrator or can be periodically invoked by a system daemon. However, these utilities are themselves vulnerable to file-based exploits and often require the use of shared directories.

Noncompliant Code Example (predictability)

This noncompliant code example hardcodes the name of a temporary file; consequently, the file's name is predictable. Even though there is a built-in check to detect whether a file still exists after its creation, this check creates a TOCTOU race condition that an attacker can exploit, by altering or deleting the file between the check and the read.

Temporary files are files and consequently must conform to the requirements specified by other rules governing operations on files, including FIO00-J. Do not operate on files in shared directories and FIO01-J. Create files with appropriate access permissions. Temporary files have the additional requirement that they must be removed before program termination.

Removing temporary files when they are no longer required allows file names and other resources (such as secondary storage) to be recycled. Each program is responsible for ensuring that temporary files are removed during normal operation. There is no surefire method that can guarantee the removal of orphaned files in the case of abnormal termination, even in the presence of a finally block, because the finally block may fail to execute. For this reason, many systems employ temporary file cleaner utilities to sweep temporary directories and remove old files. Such utilities can be invoked manually by a system administrator or can be periodically invoked by a system process. However, these utilities are themselves frequently vulnerable to file-based exploits.

Noncompliant Code Example

This and subsequent code examples assume that files are created in a secure directory in compliance with FIO00-J. Do not operate on files in shared directories and are created with proper access permissions in compliance with FIO01-J. Create files with appropriate access permissions. Both requirements may be managed outside the Java Virtual Machine (JVM).

This noncompliant code example fails to remove the file upon completion:

Code Block
bgColor#FFcccc
class TempFile {
  public static void main(String[] args) throws IOException{
    File f = new File("tempnam.tmp");
    if (f.exists()) {
      System.out.println("This file already exists");
      return;
    }

    FileOutputStream fop = null;
    try {
      fop = new FileOutputStream(f);
      String str = "Data";
      fop.write(str.getBytes());
    } finally {
      if (fop != null) {
        try {
          fop.close();
        } catch (IOException x) {
          // Handle error
        }
      }
    }
  }
}

Noncompliant Code Example (createTempFile(), deleteOnExit())

This noncompliant code example invokes the File.createTempFile() method, which generates a unique temporary file name based on two parameters: a prefix and an extension. This is the only method from Java 6 and earlier that is designed to produce unique file names, although the names produced can be easily predicted. A random number generator can be used to produce the prefix if a random file name is required.

This example also uses the deleteOnExit() method to ensure that the temporary file is deleted when the JVM terminates. However, according to the Java API [API 2014] Class File, method deleteOnExit() documentation,

Deletion will be attempted only for normal termination of the virtual machine, as defined by the Java Language Specification. Once deletion has been requested, it is not possible to cancel the request. This method should therefore be used with care.
Note: this method should not be used for file-locking, as the resulting protocol cannot be made to work reliably.

Consequently, the file is not deleted if the JVM terminates unexpectedly. A longstanding bug on Windows-based systems, reported as Bug ID: 4171239 [SDN 2008], causes JVMs to fail to delete a file when deleteOnExit() is invoked before the associated stream or RandomAccessFile is closed.

Code Block
bgColor#FFcccc
class TempFile {
  public static void main(String[] args) throws IOException{
    File f = File.createTempFile("tempnam",".tmp");
    FileOutputStream fop = null;
    try {
      fop = new FileOutputStream(f);
      String str = "Data";
      fop.write(str.getBytes());
      fop.flush();
    } finally {
      // Stream/file still open; file will
      // not be deleted on Windows systems
      f.deleteOnExit(); // Delete the file when the JVM terminates

      if (fop != null) {
        try {
          fop.close();
        } catch (IOException x) {
          // Handle error
        }
      }
Code Block
bgColor#FFcccc

class TempFile{
  public static void main(String[] args) throws IOException{
    File f = new File("tempnam.tmp");
    FileOutputStream fop = new FileOutputStream(f);
    String str = "Data";
    
    if (f.exists()) {
      fop.write(str.getBytes());
      fop.close();
    } else { 
      System.out.println("This file does not exist"); 
    }
  }      

}

Noncompliant Code Example (createTempFile(), deleteOnExit())

This noncompliant code example improves over the previous noncompliant code example by using the method File.createTempFile() to generate a unique temporary filename based on two parameters, a prefix and an extension. This is the only method currently designed and provided for producing unique file names; unfortunately, the names produced can be easy to predict. Mitigate this vulnerability by using a good random number generator to produce the prefix.

Wiki Markup
This example also attempts to use the {{deleteOnExit()}} method to ensure that the temporary file is deleted when the JVM terminates. However, according to the Java API \[[API 2006|AA. Bibliography#API 06]\] Class {{File}}, method {{deleteOnExit()}} documentation:

Deletion will be attempted only for normal termination of the virtual machine, as defined by the Java Language Specification. Once deletion has been requested, it is not possible to cancel the request. This method should consequently be used with care.
Note: this method should not be used for file-locking, as the resulting protocol cannot be made to work reliably.

Wiki Markup
Thus the file is not deleted if the JVM terminates unexpectedly. A longstanding bug on Windows based systems --- \[[Bug ID: 4171239|http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4171239]\] --- causes JVMs to fail to delete a file when {{deleteOnExit()}} is invoked before the associated stream or {{RandomAccessFile}} is closed. 

Code Block
bgColor#FFcccc

class TempFile {
  public static void main(String[] args) throws IOException{
    File f = File.createTempFile("tempnam",".tmp");
    FileOutputStream fop = new FileOutputStream(f);
    String str = "Data";
    try {
      fop.write(str.getBytes());
      fop.flush();        
    }finally {
      // Stream/file still open; file will
      // not be deleted on Windows systems
      f.deleteOnExit(); // Delete the file when the JVM terminates
    }
  }       
}

Noncompliant Code Example (POSIX, Java 1.7)

This noncompliant code example creates a temporary file using the newest features of Java 1.7's NIO facility. It uses the createTempFile() method, which creates an unpredictable name. (The actual method by which the name is created is implementation-defined and undocumented.) The file is specifically created with POSIX file permissions denying access to the file to everyone except the file's creator, a similar permission set can be devised for Windows. Furthermore, the createTempFile() will throw an exception if the file already existed. The file is opened using the try-with-resources construct, which automatically closes the file whether or not an exception occurs. And finally, the file is opened with the Java 1.7 DELETE_ON_CLOSE option, which serves to remove the file automatically when it is closed.

Code Block
bgColor#FFcccc

class TempFile {
  public static void main(String[] args) {
    // POSIX file permissions for exclusive read/write
    Set<PosixFilePermission> perms = PosixFilePermissions.fromString("rw-------");
    FileAttribute att = PosixFilePermissions.asFileAttribute( perms);
    Path tempFile = null;
    try {
      tempFile = Files.createTempFile( "file", ".myapp", att);
      try (BufferedWriter writer = Files.newBufferedWriter(tempFile, Charset.forName("UTF8"),
                                                           StandardOpenOption.DELETE_ON_CLOSE)) {
          // write to file
      }
      System.out.println("Temporary file write done, file erased");
    } catch (FileAlreadyExistsException x) {
      System.err.println("File exists: " + tempFile);
    } catch (IOException x) {
      // Some other sort of failure, such as permissions.
      System.err.println("Error creating temporary file: " + x);
    }
  }
}

Despite the new Java 1.7 features, this example still has several vulnerabilities. There is no mechanism to open the file with exclusive access, a feature provided by standard POSIX. Consequently the temporary file, once created, is still accessible and modifiable by anyone with access to its containing directory. Also, because the creation of the file and the opening of the file are distinct operations, this program is still vulnerable to a time-of-check-time-of-use (TOCTOU) race condition.

Wiki Markup
To work around the file/stream termination issue, always attempt to terminate the resource normally before invoking {{deleteOnExit()}}. Using {{File.io.delete()}} to immediately delete the file is good practice, when possible; this avoids improper JVM termination related issues. Moreover, although unreliable, {{System.gc()}} may be invoked to free up related resources. Sometimes, the resources to be deleted cannot be closed first; see, for example, \[[Bug ID: 4635827|http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4635827]\]. There is no known workaround for this case.

Compliant Solution (POSIX, Java 1.7, secure directory)

Because of the potential for race conditions, and the inherent accessability of a temporary file, temporary files must only be created in secure directories. This compliant solution depends on an isSecureDir() method. This method ensures that file and all directories above it are owned by either the user or the superuser, that each directory does not have write access for any other users, and that directories above path may not be deleted or renamed by any other users. When checking directories, it is important to traverse from the root to the leaf to avoid a dangerous race condition whereby an attacker who has privileges to at least one of the directories can rename and recreate a directory after the privilege verification.

The file name passed to this function is first verified to be a directory, and rendered absolute if necessary. If the path contains any symbolic links, this routine will recursively invoke itself on the linked-to directory and ensure it is also secure. A symlinked directory may be secure if both its source and linked-to directory are secure. The function checks every directory in the path, ensuring that every directory is owned by the current user or the superuser and that all directories in the path forbid other users from deleting or renaming files.

POSIX maintains that a directory can prevent modifications to its files either by disabling group and other write access to the directory or by turning on the sticky bit. However, Java 1.7 provides no mechanism for accessing the sticky bit.

Most home directories are secure by default on standard POSIX installations, and only shared directories such as /tmp are insecure. However, since any user can create an insecure directory, a program should verify that a particular directory is actually secure, as it may not have the permissions required to create a secure directory.

Note that this method is only effective on filesystems that are fully compatible with UNIX permissions, and it may not behave normally for filesystems with other permission mechanisms, such as AFS.

Code Block
bgColor#ccccff

public static boolean isSecureDir(Path file) {
  if (!Files.isDirectory( file)) {
    return false;
  }
  if (!file.isAbsolute()) {
    file = file.toAbsolutePath();
  }

  // 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 (!isSecureDir( Files.readSymbolicLink( partialPath))) {
          // Symbolic link, linked-to dir not secure
          return false;
        }
      } else {
        FileSystem fileSystem = partialPath.getFileSystem();
        UserPrincipalLookupService upls = fileSystem.getUserPrincipalLookupService();
        UserPrincipal root = upls.lookupPrincipalByName("root");
        UserPrincipal user = upls.lookupPrincipalByName( System.getProperty("user.name"));
        UserPrincipal owner = Files.getOwner( partialPath);
        if (!owner.equals( user) && !owner.equals( root)) {
          // 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;
}

This compliant solution uses the isSecureDir() method to ensure that an attacker may not tamper with the file to be opened and subsequently removed. Note that once the path name of a directory has been checked using isSecureDir(), all further file operations on that directory must be performed using the same path.

Code Block
bgColor#ccccff

class TempFile {
  public static boolean isSecureDir(Path file) {
    // ...
  }

  public static void main(String[] args) {
    Path tempDir = new File(args[0]).toPath();
    if (!isSecureDir( tempDir)) {
      System.out.println("Temporary Directory not secure");
      return;
    }

    // POSIX file permissions for exclusive read/write
    Set<PosixFilePermission> perms = PosixFilePermissions.fromString("rw-------");
    FileAttribute att = PosixFilePermissions.asFileAttribute( perms);
    Path tempFile = null;
    try {
      tempFile = Files.createTempFile( tempDir, "file", ".myapp", att);
      try (BufferedWriter writer = Files.newBufferedWriter(tempFile, Charset.forName("UTF8"),
                                                           StandardOpenOption.DELETE_ON_CLOSE)) {
          // write to file
        }
      System.out.println("Temporary file write done, file erased");

    } catch (FileAlreadyExistsException x) {
      System.err.println("File exists: " + tempFile);
    } catch (IOException x) {
      // Some other sort of failure, such as permissions.
      System.err.println("Error creating temporary file: " + x);
    }
  }
}

Risk Assessment

Failure to follow best practices while creating, using and deleting temporary files can lead to denial of service vulnerabilities, misinterpretations and alterations in control flow.

Rule

Severity

Likelihood

Remediation Cost

Priority

Level

FIO07-J

high

probable

medium

P12

L1

Related Vulnerabilities

GERONIMO-3489

Other Languages

This rule appears in the C Secure Coding Standard as FIO43-C. Do not create temporary files in shared directories.

This rule appears in the C++ Secure Coding Standard as FIO43-CPP. Do not create temporary files in shared directories.

Related Guidelines

<ac:structured-macro ac:name="unmigrated-wiki-markup" ac:schema-version="1" ac:macro-id="f4094dfa-a03c-4eb9-a020-ada6186b8499"><ac:plain-text-body><![CDATA[

[[MITRE 2009

AA. Bibliography#MITRE 09]]

[CWE ID 459

http://cwe.mitre.org/data/definitions/459.html] "Incomplete Cleanup"

]]></ac:plain-text-body></ac:structured-macro>

 

CWE ID 377 "Insecure Temporary File"

Bibliography

<ac:structured-macro ac:name="unmigrated-wiki-markup" ac:schema-version="1" ac:macro-id="6da856dc-f19a-49ab-83fe-eb7f41bb9f0e"><ac:plain-text-body><![CDATA[

[[API 2006

AA. Bibliography#API 06]]

Class File, methods createTempFile, delete, deleteOnExit

]]></ac:plain-text-body></ac:structured-macro>

<ac:structured-macro ac:name="unmigrated-wiki-markup" ac:schema-version="1" ac:macro-id="cda231a2-ed28-4ff2-8b76-8485b03f6fbb"><ac:plain-text-body><![CDATA[

[[Darwin 2004

AA. Bibliography#Darwin 04]]

11.5 Creating a Transient File

]]></ac:plain-text-body></ac:structured-macro>

<ac:structured-macro ac:name="unmigrated-wiki-markup" ac:schema-version="1" ac:macro-id="4bd79e06-07a2-4c9f-90bb-832d1c796813"><ac:plain-text-body><![CDATA[

[[J2SE 2011

AA. Bibliography#J2SE 11]]

 

]]></ac:plain-text-body></ac:structured-macro>

<ac:structured-macro ac:name="unmigrated-wiki-markup" ac:schema-version="1" ac:macro-id="b2d87d85-272a-4567-804f-8de9ae0a8d0d"><ac:plain-text-body><![CDATA[

[[SDN 2008

AA. Bibliography#SDN 08]]

Bug IDs: 4171239, 4405521, 4635827, 4631820

]]></ac:plain-text-body></ac:structured-macro>

<ac:structured-macro ac:name="unmigrated-wiki-markup" ac:schema-version="1" ac:macro-id="bff68c3a-aec5-4f51-b829-e77fb60eaaa0"><ac:plain-text-body><![CDATA[

[[Secunia 2008

AA. Bibliography#Secunia 08]]

[Secunia Advisory 20132

http://secunia.com/advisories/20132/]

]]></ac:plain-text-body></ac:structured-macro>

<ac:structured-macro ac:name="unmigrated-wiki-markup" ac:schema-version="1" ac:macro-id="0ed19790-25ed-4018-abb2-0bff75f6b3c3"><ac:plain-text-body><![CDATA[

[[CVE 2008

AA. Bibliography#CVE 08]]

[CVE-2008-5354

http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2008-5354]

]]></ac:plain-text-body></ac:structured-macro>

Compliant Solution (DELETE_ON_CLOSE)

This compliant solution creates a temporary file using several methods from Java's NIO.2 package (introduced in Java SE 7). It uses the createTempFile() method, which creates an unpredictable name. (The actual method by which the name is created is implementation-defined and undocumented.) The file is opened using the try-with-resources construct, which automatically closes the file regardless of whether an exception occurs. Finally, the file is opened with the DELETE_ON_CLOSE option, which removes the file automatically when it is closed.

Code Block
bgColor#ccccff
class TempFile {
  public static void main(String[] args) {
    Path tempFile = null;
    try {
      tempFile = Files.createTempFile("tempnam", ".tmp");
      try (BufferedWriter writer =
          Files.newBufferedWriter(tempFile, Charset.forName("UTF8"),
                                  StandardOpenOption.DELETE_ON_CLOSE)) {
        // Write to file
      }
      System.out.println("Temporary file write done, file erased");
    } catch (FileAlreadyExistsException x) {
      System.err.println("File exists: " + tempFile);
    } catch (IOException x) {
      // Some other sort of failure, such as permissions.
      System.err.println("Error creating temporary file: " + x);
    }
  }
}

Compliant Solution

When a secure directory for storing temporary files is not available, the vulnerabilities that result from using temporary files in insecure directories can be avoided by using alternative mechanisms, including

  • Other IPC mechanisms such as sockets and remote procedure calls.
  • The low-level Java Native Interface (JNI).
  • Memory-mapped files.
  • Threads to share heap data within the same JVM (applies to data sharing between Java processes only).

Risk Assessment

Failure to remove temporary files before termination can result in information leakage and resource exhaustion.

Rule

Severity

Likelihood

Remediation Cost

Priority

Level

FIO03-J

Medium

Probable

Medium

P8

L2

Automated Detection

ToolVersionCheckerDescription
Parasoft Jtest
Include Page
Parasoft_V
Parasoft_V
CERT.FIO03.ATF
CERT.FIO03.REMTMP
Avoid temporary files
Remove temporary files before termination

Related Guidelines

Bibliography

[API 2014]

Class File
   Method createTempFile
   Method delete
   Method deleteOnExit

[Darwin 2004]

Section 11.5, "Creating a Transient File"

[J2SE 2011]


[JDK Bug 2015]

Bug JDK-4405521
Bug JDK-4631820

[SDN 2008]Bug ID: 4171239 

[Secunia 2008]

Secunia Advisory 20132


...

Image Added Image Added Image AddedFIO06-J. Ensure all resources are properly closed when they are no longer needed      12. Input Output (FIO)      FIO08-J. Do not log sensitive information outside a trust boundary