Open In App

Standard Practices for Protecting Sensitive Data in Java

Improve
Improve
Like Article
Like
Save
Share
Report

The Java platform provides an environment for executing code with different permission levels and a robust basis for secure systems through features such as memory safety. The Java language and virtual machine make application development highly resistant to common programming mistakes, stack smashing, and buffer overflow attacks which are possible in the C and C++ programming language. However, bugs produced through vulnerable programming practices can have serious security ramifications and could appear in any layer of the stack. In this article, we will list some standard programming practices in Java to protect sensitive data.

Secure coding guidelines contain the following parameters which are as follows:

  1. Releasing Resources
  2. Destroy sensitive information from exceptions
  3. Avoid Dynamic SQL
  4. Limiting Accessibility
  5. Limiting Extensibility

Let us discuss each of them how these practices do help in protecting our sensitive data in java and will be discussing with help of sample codes where ever required for better understandability.

1. Releasing Resources 

Application resources used during execution, such as open files, memory, objects, and locks if not released after their use could cause errors, duplication, and deadlocks during processing. Therefore, resources should always be released after their use by incorporating techniques like Execute Around Method (using lambda) and try-with-resource syntax.

Example 1: Illustrate Execute Around Methods

// Main class
class GFG {

    // Main driver method
    public static void main(String[] args)
    {

        // Inserting code here

        // Execute Around pattern using Lambda
        // Clean-up is taken care of by lambda

        // Out lambda expression
        long sum = readFileBuffered(InputStream in -> {
            long current = 0;
            for (;;) {
                int b = in.read();
                if (b == -1) {
                    return current;
                }

                current += b;
            }
        });
    }
}

Example 2: Illustrating try-with-resources syntax

// Main class
class GFG {

    // Main driver method
    public static void main(String[] args)
    {

        // Inserting code here

        // Try-with-resource syntax
        public R readFileBuffered(
            InputStreamHandler handler) throws IOException
        {

            // try statement with one or more resources
            try (final InputStream in
                 = Files.newInputStream(path)) {
                handler.handle(new BufferedInputStream(in));
            }

            // By now the resource is closed
            // at the end of the statement execution
        }
    }
}

2. Destroy sensitive information from exceptions

Many forms of attacks involve guessing or knowing the location of files, and exceptions may include sensitive information about the configuration and internals of the system. Stack traces of thrown exceptions such as java.io.FileNotFoundException can include the file paths of the system which can be used by attackers to backtrack to system files and target sensitive data. Therefore, internal exceptions should be caught and sanitized before propagating them to upstream callers.

3. Avoid Dynamic SQL

Dynamically created SQL statements from the untrusted sources can contain input that is subject to command injection, which means an injection of malicious code through SQL statements to directly affect data. Instead, parameterized SQL statements should be used such as java.sql.PreparedStatement or java.sql.CallableStatement so that the statements are pre-compiled and only parameters are needed to be inserted in order for them to execute.

Sample 

String sql = "SELECT * FROM User WHERE userId = ?"; 
PreparedStatement stmt = con.prepareStatement(sql); 
stmt.setString(1, userId); 
ResultSet rs = prepStmt.executeQuery();

4. Limiting Accessibility

Class members, methods, constructors, and interfaces should be declared as private or protected if they are not a part of an API to avoid exposing their implementation. If the implementations are encapsulated and not made public to outside entities then security is maintained as outsiders will not be able to change the internal structure.

5. Limiting Extensibility

If classes or methods are not declared final then an attacker can maliciously override them. A class that does not permit inheritance is easier to implement and verify that it is secure. Prefer composition to inheritance.

Example Limiting Extensibility through Composition and final class

// Class 1
// Helper class- Book class
class Book {

    // Member variables of Book class
    public String title;
    public String author;

    // Parameterized constructor of Book class
    Book(String title, String author)
    {
        // This refers to current object itself
        this.title = title;
        this.author = author;
    }
}

// Class 2
// Helper class
// Unsubclassable class Library with composed behavior.
public final class Library {

    // Composition
    private final List<Book> books;

    // Hiding the constructor of this class
    // by declaring it private
    private Library(Book book)
    {
        books = new ArrayList<Book>();
    }

    // Method of this class
    // Guarded Construction of class methods
    private void createLibrary(Book book)
    {
        // ...validate any arguments...

        // ...perform security checks...

        // adding elements to the book
        books.add(book);
    }
} 

In this article, we addressed some common pitfalls and the surrounding solutions. Hence, the most effective approach to minimizing vulnerabilities is to have obviously no flaws rather than no obvious flaws. These were some many practices that a Java programmer can adhere to in order to produce secure Java applications. 


Last Updated : 13 Dec, 2022
Like Article
Save Article
Previous
Next
Share your thoughts in the comments
Similar Reads