...
File identification is less of an issue if applications maintain their files in secure directories, where they can only be accessed by the owner of the file and (possibly) by a system administrator.
Noncompliant Code Example
In this noncompliant code example, the file identified by the string filename
is opened, processed, closed, and then reopened for reading.
Code Block | ||
---|---|---|
| ||
//Identify a file by its path
String filename = "...";
Path file1 = Paths.get(filename);
// Open the file for writing
BufferedWriter bw = new BufferedWriter(
new OutputStreamWriter(Files.newOutputStream(file1)));
bw.write(...);
// Close the file
bw.close();
/*
* A race condition here allows for an attacker to switch
* out the file for another
*/
// Reopen the file for reading
Path file2 = Paths.get(filename);
BufferedReader br = new BufferedReader(
new InputStreamReader(Files.newInputStream(file2)));
String line;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
// Close the file
br.close();
|
There is no guarantee that the file opened for reading is the same file that is opened for writing. An attacker can replace the original file (for example, with a symbolic link) between the first call to close()
and the subsequent creation of the BufferedReader
.
Noncompliant Code Example (isSameFile()
)
In this noncompliant code example, the programmer attempts to ensure that the file opened for reading is the same as the file previously opened for writing by calling the method isSameFile()
.
Code Block | ||
---|---|---|
| ||
//Identify a file by its path
String filename = "...";
Path file1 = Paths.get(filename);
// Open the file for writing
BufferedWriter bw = new BufferedWriter(
new OutputStreamWriter(Files.newOutputStream(file1)));
bw.write(...);
// Close the file
bw.close();
/*
* A race condition here allows for an attacker to switch
* out the file for another
*/
// Reopen the file for reading
Path file2 = Paths.get(filename);
if (!Files.isSameFile(file1, file2)) {
System.out.println("File tampered with");
// Deal with error
}
BufferedReader br = new BufferedReader(
new InputStreamReader(Files.newInputStream(file2)));
String line;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
// Close the file
br.close();
|
...
That is, isSameFile()
may simply check that the paths to the two files are the same.
Compliant Solution (Multiple Attributes)
This compliant solution checks the creation and last modified times of the files to ensure that the file opened for reading is the same file as the file that was written.
Code Block | ||
---|---|---|
| ||
//Identify a file by its path
String filename = "...";
Path file1 = Paths.get(filename);
// Open the file for writing
BufferedWriter bw = new BufferedWriter(
new OutputStreamWriter(Files.newOutputStream(file1)));
bw.write(...);
// Close the file
bw.close();
/*
* A race condition here allows for an attacker to switch
* out the file for another
*/
// Reopen the file for reading
Path file2 = Paths.get(filename);
BasicFileAttributes attr1 = Files.readAttributes(file1, BasicFileAttributes.class);
BasicFileAttributes attr2 = Files.readAttributes(file2, BasicFileAttributes.class);
FileTime creation1 = attr1.creationTime();
FileTime modified1 = attr1.lastModifiedTime();
FileTime creation2 = attr2.creationTime();
FileTime modified2 = attr2.lastModifiedTime();
if ( (!creation1.equals(creation2)) || (!modified1.equals(modified2)) ) {
System.out.println("File tampered with");
// Deal with error
}
BufferedReader br = new BufferedReader(
new InputStreamReader(Files.newInputStream(file2)));
String line;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
// Close the file
br.close();
|
Although this solution is reasonably secure, a determined attacker could create a symbolic link with the same creation and last-modified times as the original file.
Compliant Solution (POSIX fileKey
Attribute)
In environments that support the fileKey
attribute, a more reliable approach is to check that the fileKey
attributes of the two files are the same, as shown in this compliant solution.
Code Block | ||
---|---|---|
| ||
//Identify a file by its path
String filename = "...";
Path file1 = Paths.get(filename);
// Open the file for writing
BufferedWriter bw = new BufferedWriter(
new OutputStreamWriter(Files.newOutputStream(file1)));
bw.write(...);
// Close the file
bw.close();
/*
* A race condition here allows for an attacker to switch
* out the file for another
*/
// Reopen the file for reading
Path file2 = Paths.get(filename);
BasicFileAttributes attr1 = Files.readAttributes(file1, BasicFileAttributes.class);
BasicFileAttributes attr2 = Files.readAttributes(file2, BasicFileAttributes.class);
Object key1 = attr1.fileKey();
Object key2 = attr2.fileKey();
if ( !key1.equals(key2) ) {
System.out.println("File tampered with");
// Deal with error
}
BufferedReader br = new BufferedReader(
new InputStreamReader(Files.newInputStream(file2)));
String line;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
// Close the file
br.close();
|
This approach will not work on all platforms. For example, on an Intel Core i5-2400 machine running Windows 7 Enterprise, all fileKey
attributes are null.
Risk Assessment
Many file-related vulnerabilities are exploited to cause a program to access an unintended file. Proper file identification is necessary to prevent exploitation.
Guideline | Severity | Likelihood | Remediation Cost | Priority | Level |
---|---|---|---|---|---|
FIO52-J JG | medium | probable | medium | P8 | L2 |
Automated Detection
In general, it is not possible to automatically detect this problem.
Related
...
Guidelines
C Secure Coding Standard:
Search for vulnerabilities resulting from the violation of this guideline on the CERT website.
Other Languages
This guideline is based on FIO05-C. Identify files using multiple file attributes.