...
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 (
...
Temporary file)
This noncompliant code example hardcodes provides the name of a temporary file ; consequently, the file's name is predictableto create. Even though there is a built-in check to detect the program detects 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 | ||
---|---|---|
| ||
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");
}
}
}
|
...
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.
Providing unique filenames is a common attempt at mitigating the risk of creating a file in an insecure 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 exploit the filesystem as in the last example.
Wiki Markup |
---|
This example also attempts to use the {{deleteOnExit() |
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: |
...
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 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.
...
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.
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.
On POSIX systems, disabling group and other write access to a directory prevents modification by anyone other than the owner of the directory and the system administrator.
Most home directories are secure by default on standard POSIX installations, and only shared directories such as /tmp
are insecure. However, because 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.
...
bgColor | #ccccff |
---|
...
)
This compliant solution uses the isSecureDirisInSecureDir()
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 isSecureDirisInSecureDir()
, all further file operations on that directory must be performed using the same path.
Code Block | ||
---|---|---|
| ||
class TempFile { public static boolean isSecureDirisInSecureDir(Path file) { // ... } public static void main(String[] args) { Path tempDir = new File(args[0]).toPath(); if (!isSecureDirisInSecureDir(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); } } } |
...
<ac:structured-macro ac:name="unmigrated-wiki-markup" ac:schema-version="1" ac:macro-id="fb6463c73f9857ef-15b31221-438646d2-87638e16-5ee6796f17e0f34146b3ce5f"><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="f04ecf9b6993a1be-23b9728a-48c74ae3-95b09acf-100a5b430a43231b9d449d03"><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="51c6b131ee420005-2f286e83-469d4744-ab239117-8a840ce67f6992972f3c3edd"><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="db7b3aa9639ffbe4-7f6492e1-42d9478f-93e2a64c-ac06f548dc60884bfddc4ae7"><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="4972cd6cc1a5b27a-fa76b3cb-405e4518-8ee2a3b1-1cc9f56e77bb84865f930366"><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="ec1fb41c2d9a9871-cc28d49c-40224841-b911b66e-8a7bfb9a232931c8b4215043"><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> |
...