...
Code Block |
---|
|
public void fio52processFile_nce1nce(String filename){
// Identify a file by its path
Path file1 = Paths.get(filename);
// Open the file for writing
try(BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(Files.newOutputStream(file1)))) {
// Write to file...
} catch (Exception e) {
System.out.println("Exception during file access" + e);
}
// Close the file
/*
* 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);
try(BufferedReader br = new BufferedReader(new InputStreamReader(Files.newInputStream(file2)))){
String line;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
} catch (Exception e) {
System.out.println("Exception during file access" + e);
}
}
|
There is no guarantee that the file opened for reading is the same file that was 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
.
...
Code Block |
---|
|
public void fio52sameFile_nce2nce(String filename){
// Identify a file by its path
Path file1 = Paths.get(filename);
// Open the file for writing
try(BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(Files.newOutputStream(file1)))) {
// Write to file
} catch (Exception e) {
System.out.println("Exception during file access" + e);
}
// ...
// Reopen the file for reading
Path file2 = Paths.get(filename);
if (!Files.isSameFile(file1, file2)) {
System.out.println("File tampered with");
// Handle error
}
try(BufferedReader br = new BufferedReader(new InputStreamReader(Files.newInputStream(file2)))) {
String line;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
} catch (Exception e) {
System.out.println("Exception during file access" + e);
}
} |
Unfortunately, there is no guarantee that the method isSameFile()
really checks that the files are the same file. The Java 7 API for isSameFile()
says:
...
Code Block |
---|
|
public void fio52sameFile_cs1cs(String filename) throws IOException{
// Identify a file by its path
Path file1 = Paths.get(filename);
BasicFileAttributes attr1 = Files.readAttributes(file1, BasicFileAttributes.class);
FileTime creation1 = attr1.creationTime();
FileTime modified1 = attr1.lastModifiedTime();
// Open the file for writing
try(BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(Files.newOutputStream(file1)))) {
// Write to file...
} catch (Exception e){
System.out.println("Exception during file access" + e);
}
// Reopen the file for reading
Path file2 = Paths.get(filename);
BasicFileAttributes attr2 = Files.readAttributes(file2, BasicFileAttributes.class);
FileTime creation2 = attr2.creationTime();
FileTime modified2 = attr2.lastModifiedTime();
if ( (!creation1.equals(creation2)) || (!modified1.equals(modified2)) ) {
System.out.println("File tampered with");
// Handle error
}
try(BufferedReader br = new BufferedReader( new InputStreamReader(Files.newInputStream(file2)))){
String line;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
} catch (Exception e){
System.out.println("Exception during file access" + e);
}
}
|
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. Also, a time-of-check, time-of-use (TOCTOU) race condition occurs between the time the file's attributes are first read and the time the file is first opened. Likewise, a second TOCTOU condition occurs the second time the attributes are read and the file is reopened.
...
Code Block |
---|
|
public void fio52filekey_cs2cs(String filename) throws IOException{
// Identify a file by its path
Path file1 = Paths.get(filename);
BasicFileAttributes attr1 = Files.readAttributes(file1, BasicFileAttributes.class);
Object key1 = attr1.fileKey();
// Open the file for writing
try(BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(Files.newOutputStream(file1)))) {
// Write to file
} catch (Exception e) {
System.out.println("Exception during file access" + e);
}
// Reopen the file for reading
Path file2 = Paths.get(filename);
BasicFileAttributes attr2 = Files.readAttributes(file2, BasicFileAttributes.class);
Object key2 = attr2.fileKey();
if ( !key1.equals(key2) ) {
System.out.println("File tampered with");
// Handle error
}
try(BufferedReader br = new BufferedReader(new InputStreamReader(Files.newInputStream(file2)))) {
String line;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
} catch (Exception e) {
System.out.println("Exception during file access" + e);
}
} |
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.
...
Code Block |
---|
|
public void fio52randomAccess_cs3cs(String filename) throws IOException{
// Identify a file by its path
RandomAccessFile file = new RandomAccessFile( filename, "rw");
// Write to file...
// Go back to beginning and read contents
file.seek(0);
try {
while (true) {
String s = file.readUTF();
System.out.print(s);
}
} catch (EOFException x) {
// Ignore, this breaks out of while loop
}
br.close();
} |
Noncompliant Code Example (file size)
...
Code Block |
---|
|
static long goodSize = 1024;
public void doSomethingWithFile(String filename) {
long size = new File( filename).length();
if (size != goodSize) {
System.out.println("File is wrong size!");
return;
}
try (BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream( filename)))) {
// ... work with file
} catch (Exception e) {
System.out.println("Exception during file access" + e);
}
} |
...
Code Block |
---|
|
static long goodSize = 1024;
public void doSomethingWithFile(String filename) {
try (FileInputStream in = new FileInputStream( filename);
BufferedReader br = new BufferedReader(new InputStreamReader(in))) {
long size = in.getChannel().size();
if (size != goodSize) {
System.out.println("File is wrong size!");
return;
}
String line;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
} catch (Exception e) {
System.out.println("Exception during file access" + e);
}
} |
...