...
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 attacker 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 example, 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 operating on a file determined by the attacker. This exploit is particularly dangerous when the vulnerable program is running with elevated privileges. On multiuser systems a user can be tricked by an attacker into unintentionally operating on their own files. Temporary files are files and consequently must conform to the requirements specified by rules governing operations on files including FIO00-J. Do not overwrite an existing file while attempting to create a new file, FIO03-J. Create files with appropriate access permissions, and Consequently, temporary file management must comply with FIO04-J. Do not operate on files in shared directories. Temporary files have an 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 daemon. However, these utilities are themselves vulnerable to file-based exploits and may require the use of secure directories.
Noncompliant Code Example (createTempFile()
, deleteOnExit()
)
This noncompliant code example invokes the File.createTempFile()
method 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; although the names produced can be easily predicted. This can be solved by using a good random number generator to produce the prefix.
Providing unique filenames is a common attempt at mitigating the risk of creating a file in an insecure or shared directory. If the filename is not sufficiently unique or random, an attacker can guess or predict the name of the file to be created, and create a symbolic link with the same name, the final target of which is a file selected by the attacker.
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 |
---|
Consequently, the file is not deleted if the JVM terminates unexpectedly. A longstanding bug on Windows based systems reported as [Bug ID: 4171239|http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4171239] \[[SDN 2008|AA. Bibliography#SDN 08]\] causes JVMs to fail to delete a file when {{deleteOnExit()}} is invoked before the associated stream or {{RandomAccessFile}} is closed. |
Code Block | ||
---|---|---|
| ||
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), DELETE_ON_CLOSE
)
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. Additionally, 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. 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 | ||
---|---|---|
| ||
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 and avoids improper JVM termination related issues. 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)
This compliant solution uses the isInSecureDir()
method to ensure that an attacker cannot tamper with the temporary file. Note that once the path name of a directory has been checked using isInSecureDir()
, all further file operations on that directory must be performed using the same path name.
Code Block | ||
---|---|---|
| ||
class TempFile {
public static boolean isInSecureDir(Path file) {
// ...
}
public static void main(String[] args) {
Path tempDir = new File(args[0]).toPath();
if (!isInSecureDir(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);
}
}
}
|
Compliant Solution
Vulnerabilities that result from using shared files can be avioded by using other 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)
- a secure directory that can be accessed only by application instances, provided that multiple instances of the application running on the same platform avoid competing for the same files.
Exceptions
...
Many programs that create temporary files attempt to give them unique and unpredictable file names. This is a common attempt at mitigating the risk of creating a file in an insecure or shared directory. If the filename is not sufficiently unique or random, an attacker can guess or predict the name of the file to be created, and create a symbolic link with the same name, the final target of which is a file selected by the attacker. However, if a temporary file is created in a secure directory, an attacker cannot tamper with the file, and so the need for unpredictable names is eliminated.
Temporary files are files and consequently must conform to the requirements specified by other rules governing operations on files including FIO00-J. Do not overwrite an existing file while attempting to create a new file, and FIO03-J. Create files with appropriate access permissions. Furthermore, temporary files have an 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 daemon. However, these utilities are themselves vulnerable to file-based exploits and may require the use of secure directories.
We will assume that the code samples shown below obey all these rules: That is, the files are created in the current directory of the program, which is assumed to be a secure directory. Also, the files are created with appropriate access permissions, which are managed by default outside Java. The code samples below make sure that no existing file is overwritten.
Noncompliant Code Example
This noncompliant code example hardcodes the name of a temporary file; consequently, the file's name is predictable. However, the program makes no attempt to remove the file upon completion.
Code Block | ||
---|---|---|
| ||
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");
}
}
}
h2. Noncompliant Code Example ({{createTempFile()}}, {{deleteOnExit()}})
This noncompliant code example invokes the {{File.createTempFile()}} method which generates 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; although the names produced can be easily predicted. If the filename must be unpredictable, this problem can be solved by using a good random number generator to produce the prefix.
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:
{quote}
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.
{quote}
Consequently, the file is not deleted if the JVM terminates unexpectedly. A longstanding bug on Windows based systems reported as [Bug ID: 4171239|http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4171239] \[[SDN 2008|AA. Bibliography#SDN 08]\] causes JVMs to fail to delete a file when {{deleteOnExit()}} is invoked before the associated stream or {{RandomAccessFile}} is closed.
{code: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
}
}
}
|
Compliant Solution (Java 1.7), DELETE_ON_CLOSE
)
This compliant solution creates a temporary file using several methods of Java 1.7's NIO package. It uses the createTempFile()
method, which creates an unpredictable name. (The actual method by which the name is created is implementation-defined and undocumented.) Additionally, the createTempFile()
will throw an exception if the file already exists. The file is opened using the try-with-resources construct, which automatically closes the file whether or not an exception occurs. 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 | ||
---|---|---|
| ||
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);
}
}
}
|
Risk Assessment
Failure to follow best practices while creating, using and deleting temporary files can lead to denial of service vulnerabilitiesinformation leakage, misinterpretations and alterations in control flow.
...
<ac:structured-macro ac:name="unmigrated-wiki-markup" ac:schema-version="1" ac:macro-id="c8f174726b1eca29-bf7cd470-4d494724-9713b775-4239da74b2738886247bc92b"><ac:plain-text-body><![CDATA[ | [[API 2006 | AA. Bibliography#API 06]] | Class File, methods | ]]></ac:plain-text-body></ac:structured-macro> | |
<ac:structured-macro ac:name="unmigrated-wiki-markup" ac:schema-version="1" ac:macro-id="87a854c9bf1bd66b-45175162-4e014f3b-afa29df7-dc6729c73e5507b37f254548"><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="6a0456f1786e04db-aa93a2dd-437c42a3-81e48977-5384f08e01800a146ac9dd13"><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="d782db02778291dc-929c8976-409b4ec9-85999f45-801b3c08458dff9ef5dacb54"><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="57cdf80117cff053-d849e016-41134831-8f87bab8-97017117ee11f3e6da2b879b"><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="21a5f6ed5f5401bf-f9df4138-4a0d4168-9b5787bd-5237ae4b9e8350712ab952fa"><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> |
...