Standard practice for protecting sensitive data in Java application
Millions of applications are getting highly affected due to the rise of cyber-attacks worldwide, and Java applications are no exception. Businesses and organizations' data can be at risk if the application is not properly secured. Securing the application from any data vulnerabilities and breaches must be an absolute priority. This article will guide you through some standard practices to secure sensitive data in Java applications.
Some of the standard practices are listed below.
- Secure the Password Storage
- Release the Resources
- Encryption: Protect the data at rest and in transit
- Avoid Dynamic SQL
- Update Dependencies Regularly
1. Secure the Password Storage
One of the most fundamental features of any application is its way of storing the password. Many junior programmers store their passwords in the form of plain text, and this can prove to be a huge mistake.
Instead of storing the passwords in a plain text, password hashing algorithms such as bcrypt, scrypt, or Argon2 can be used to store the password securely.
Now, go through the example below where we have utilized the bcrypt with the Bouncy Castle library in Java that hashes a given password. It also checks whether the hashed password is correct.
Note that the dependency given below needs to be added to the pom.xml file in your maven project to run the program below.
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.70</version>
</dependency>
FileName: SecurePasswordExample.java
import org.bouncycastle.crypto.generators.OpenBSDBCrypt;
import java.security.SecureRandom;
public class SecurePasswordExample {
// Main method
public static void main(String[] args) {
String passKey = "clientPassword";
byte[] salt = new byte[16];
// Generates the salt using a secure random number
new SecureRandom().nextBytes(salt);
// To hash the passKey with bcrypt
String hashedPassKey = OpenBSDBCrypt.generate(passKey.toCharArray(), salt, 12);
// It verifies the hashed passKey
String passkeyToVerify = "clientPassword";
boolean isPasswordCorrect = OpenBSDBCrypt.checkPassword(hashedPassKey, passkeyToVerify.toCharArray());
System.out.println("Is passKey correct: " + isPasswordCorrect);
}
}
Output 1:
Is passKey correct: true
Output 2: When passkeyToVerify = “myPassword”
Is passKey correct: false
2. Release the Resources
During code execution, the application uses multiple resources such as open files, memory, objects and locks, and if these resources are not released after their usage, it can cause deadlocks, errors, and exceptions that eventually lead to system failure. Therefore, the resources must be released after their usage, and this can be done using techniques such as Execute Around Method (using lambda) or try–with–resource syntax.
Example 1: Execute Around Method (using lambda)
// Main class
public class ExecuteAroundPattern {
// Main method
public static void main(String[] args)
{
// Inserting code here
// Demonstrate the execute Around pattern with lambda
// The Clean up is done by lambda
// Out lambda expression
long sum;
sum = readFileBuffered(InputStream in -> {
long current = 0;
for (;;) {
int i = in.read();
if (i == -1) {
return current;
}
current += i;
}
});
}
}
Example 2: try- with – resource syntax
import java.io.BufferedInputStream;
import java.io.InputStream;
import java.nio.file.Files;
class TryWithResource {
// Main 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 is
= Files.newInputStream(path)) {
handler.handle(new BufferedInputStream(is));
}
// After the execution of the statement
//The resource is closed
}
}
}
3. Encryption: Protect the data at rest and in transit
Encryption of your private and sensitive data is necessary to prevent any unauthorized access or spying of data in the applications.
Java offers many tools and libraries to implement encryption, such as Java Cryptography Extension (JCE) and Java Cryptography Architecture (JCA).
Encrypting sensitive data at rest and in transit is essential to prevent unauthorized access. Data at rest can be referred to as data present in a device or within a system when the data is not being processed or used. For example, the data stored in hard drives, databases, and other storage devices. The Data at transmit refers to data under processing, transferred, or communicated between systems and devices. For example, the data passed over the internet, among servers or between client and server.
Now, observe the example below where we have encrypted the data using the encrypting algorithm of AES – 256.
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
//...
String sampleData = "Data before the execution";
KeyGenerator genKey = KeyGenerator.getInstance("AES");
genKey.init(256);
SecretKey sKey = genKey.generateKey();
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, sKey);
byte[] encryptedData = cipher.doFinal(sampleData.getBytes());
4. Avoid Dynamic SQL
One of the most common forms of cyber attack is SQL injection attack. These SQL injection attacks occur when the attacker tries to manipulate an SQL query by inserting malicious code. The injected malicious code can run commands against the data to view, modify or delete the data. It can have severe consequences, such as data loss, system failure or even complete system compromise.
To avoid this, the parameterized SQL statements such as java.sql.PreparedStatement or java.sql.CallableStatement can be utilized so that the SQL statements can be pre–compiled, and only the statements parameter needs to be added to execute the query.
Example
String sql = "SELECT * FROM Product WHERE sku = ?";
PreparedStatement statement = con.prepareStatement(sql);
statement.setString(1, sku);
ResultSet set = statement.executeQuery();
5. Update Dependencies Regularly
The dependencies must be updated regularly as the outdated version may contain some vulnerabilities that can harm sensitive data. It may be difficult to track the versions of the dependencies and update them manually. There are automatic dependency management tools such as Apache Maven or Gradle that identify any outdated dependencies used in the application.