Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: added try-with-resources

...

Code Block
bgColor#FFcccc
//Identify a file by its path
String filename = // initialized

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
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);


try(BufferedReader br = new BufferedReader(
  new InputStreamReader(Files.newInputStream(file2))));
{
	String line;

	while ((line = br.readLine()) != null) {
    		System.out.println(line);
	}
}
//catch Close(Exception thee) file
br.close();{
	System.out.println("Exception during file access" + e);
}

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.

...

Code Block
bgColor#FFcccc
//Identify a file by its path
String filename = // initializedinitialization

Path file1 = Paths.get(filename);
 
// Open the file for writing
try(BufferedWriter bw = new BufferedWriter(
  new OutputStreamWriter(Files.newOutputStream(file1)));)
{
	// Write to file...
// Close the file
bw.close();


}
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 Close(Exception thee) file
br.close({
  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
bgColor#ccccff
//Identify a file by its path
String filename = // initialized
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 Close the file
bw.close();

// ...
(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);
	}

// Close the file
br.close();}
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, there is a time-of-check-time-of-use (TOCTOU) race condition between when the file's attributes are read and when the file is first opened. Likewise, there is another TOCTOU between the second attributes are read and the file is reopened. 

...

Code Block
bgColor#ccccff
//Identify a file by its path
String filename = // initialized

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...
// Close the file
bw.close();

// ...
}
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 Close(Exception thee) file
br.close({
		  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.

...