Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

Complete mitigation (that is, foolproof protection of data in memory) requires support from the underlying operating system and Java Virtual Machine. For example, if swapping sensitive data out to disk is an issue, a secure operating system that disables swapping and hibernation is required.

Noncompliant Code Example

This noncompliant code example reads user name and password information from the console and stores the password as a String object object. The credentials remain exposed until the garbage collector reclaims the memory associated with this String. While this method explicitly invokes System.gc() after verification, it is possible for the password to to not be garbage-collected. For example, this can happen if the password string matches a pre-existing string in the program.this String.

Code Block
bgColor#FFCCCC
class Password {
  public static void main (String args[]) throws IOException {
    Console c = System.console();
    if (c == null) {
      System.err.println("No console.");
      System.exit(1);
    }

    String username = c.readLine("Enter your user name: ");
    String password = c.readLine("Enter your password: ");

    boolean isValidUser = if (!verify(username, password); 


    // Clear the password
    password = null;
    System.gc();


    if (!isValidUser) {
      throw new SecurityException("Invalid Credentials"); 
    }

    // User is authorized, continue...
  }

  // Dummy verify method, always returns true   
  private static final boolean verify(String username, String password) {
    return true;
  }
}

Compliant Solution

This compliant solution uses the Console.readPassword() method to obtain the password from the console:

Code Block
bgColor#ccccff
class Password {
  public static void main (String args[]) throws IOException {
    Console c = System.console();
    
    if (c == null) {
      System.err.println("No console.");
      System.exit(1);
    }

    String username = c.readLine("Enter your user name: ");
    char[] password = c.readPassword("Enter your password: ");
    boolean isValidUser = verify(username, password); 


    // Clear the password
    Arrays.fill(password,' ');
    password = null;
    System.gc();


    if (!isValidUser) {
      throw new SecurityException("Invalid Credentials"); 
    }

    // User is authorized, continue...
  }

  // Dummy verify method, always returns true   
  private static final boolean verify(String username, char[] password) {
    return true;
  }
}

The Console.readPassword() method allows the password to be returned as a sequence of characters rather than as a String object. Because the password is never interned as a String, it will not survive garbage collection even if it matches another string. Consequently, the programmer can clear the password from the array immediately after use. This method

The Console.readPassword() method also disables echoing of the password to the console.

Noncompliant Code Example

This noncompliant code example uses a BufferedReader to wrap an InputStreamReader object so that sensitive data can be read from a file:

...

The BufferedReader.readLine() method returns the sensitive data as a String object, which can persist long after the data is no longer needed. The BufferedReader.read(char[], int, int) method can read and populate a char array. However, it requires the programmer to manually clear the sensitive data in the array after use. Alternatively, even if the BufferedReader were to wrap a FileReader object, it would suffer from the same pitfalls.

Compliant Solution

This compliant solution uses a directly allocated NIO (new I/O) buffer to read sensitive data from the file. The data can be cleared immediately after use and is not cached or buffered at multiple locations. It exists only in the system memory.

Code Block
bgColor#ccccff
void readData() {
  int bufferSize = 16 * 1024;
  byte zeroes = new byte[bufferSize];
  ByteBuffer buffer = ByteBuffer.allocateDirect(16 * 1024bufferSize);
  try (FileChannel rdr = (new FileInputStream("file")).getChannel()) {
   	while (rdr.read(buffer) > 0) {

      // Do something with the buffer

	  buffer.clear();
      buffer.put(zeroes); // overwrite buffer with zeroes
	  buffer.clear();
	}
  } catch (Throwable e) {
    // Handle error
  }
} 

Note that manual clearing of the buffer data is mandatory because direct buffers are not garbage collected.

Applicability

Failure to limit the lifetime of sensitive data can lead to information leaks.

Bibliography

 

...

Image Modified Image Modified Image Modified