Versions Compared

Key

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

...

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 File Names

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 because of 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

...

  • 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 (Predictable File Names)

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.

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.

...

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.

...

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] \[[SDN 2008|AA. Bibliography#SDN 08]\]. 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 shared directories, 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.

...

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

Related Guidelines

Bibliography

<ac:structured-macro ac:name="unmigrated-wiki-markup" ac:schema-version="1" ac:macro-id="0762ed3bef82dfec-e490b751-4d574d10-b4e89448-118acaae49824e6a388b774c"><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="50e0adf937597ff6-7be13d8d-43a54c7f-ae01aa76-50ed187e30cefd6dd697ac4d"><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>

<ac:structured-macro ac:name="unmigrated-wiki-markup" ac:schema-version="1" ac:macro-id="6137b9c4aac46536-a416aa5c-44a44e5a-aefda142-7f50c066cb7057f9f27301a9"><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="aaffb4869fb8736c-841e84be-422248e8-b5c5a0e0-1a68b64b286f53bd63d3899c"><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="4d946f5572da5230-aae90545-4934472d-ad26a106-60128e2007968c71ca16af92"><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="91b99dc16c96a1ea-ed8c6b69-49d04f47-8297bcfa-eb174d8db6876b76f77bec04"><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>

...