Non-encrypted confidential information - Credentials - Java

Non-encrypted confidential information - Credentials - Java

Need

Secure storage of credentials

Context

  • Usage of Java for building cross-platform applications
  • Usage of javax.servlet for building Java web applications
  • Usage of javax.servlet.http for handling HTTP requests and responses in Java Servlets

Description

Non compliant code

        import javax.servlet.*;
import javax.servlet.http.*;

public class LoginServlet extends HttpServlet {

    private static final String USERNAME = "admin";
    private static final String PASSWORD = "password123";

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String username = request.getParameter("username");
        String password = request.getParameter("password");

        if (USERNAME.equals(username) && PASSWORD.equals(password)) {
            // Authentication successful
            request.getSession().setAttribute("user", username);
            response.sendRedirect("/home");
        } else {
            // Authentication failed
            response.sendRedirect("/login");
        }
    }
}
        
        

In the above code, we have a LoginServlet class which extends HttpServlet. This servlet handles the login process of an application. The servlet takes a username and password from the HTTP request parameters and checks if it matches the predefined USERNAME and PASSWORD values.

The vulnerability lies in the fact that the credentials used for authentication ( USERNAME and PASSWORD) are hardcoded into the application's source code in plain text. This is a serious security issue as anyone who has access to the source code can see these credentials.

If an attacker can gain access to the source code, they can directly read these credentials and use them to gain unauthorized access to the system. This could lead to unauthorized data access, data manipulation, or even more serious impacts depending on the privileges associated with these credentials.

Furthermore, since these credentials are hardcoded, they cannot be changed without changing the source code and redeploying the application. This makes it impossible to rotate these credentials on a regular basis, a common practice to mitigate the risk of credential compromise.

Steps

  • Use a secure password storage mechanism such as hashing or encryption.
  • Do not store passwords in plain text in the source code.
  • Implement a secure authentication mechanism that does not rely on hard-coded credentials.
  • Use a secure session management mechanism to store user information.
  • Implement secure communication protocols such as HTTPS to protect sensitive data during transmission.

Compliant code

        import javax.servlet.*;
import javax.servlet.http.*;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.nio.charset.StandardCharsets;
import java.math.BigInteger;

public class LoginServlet extends HttpServlet {

    private static final String USERNAME = "admin";
    private static final String PASSWORD = hashPassword("password123");

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String username = request.getParameter("username");
        String password = request.getParameter("password");

        if (USERNAME.equals(username) && PASSWORD.equals(hashPassword(password))) {
            // Authentication successful
            request.getSession().setAttribute("user", username);
            response.sendRedirect("/home");
        } else {
            // Authentication failed
            response.sendRedirect("/login");
        }
    }

    private static String hashPassword(String password) {
        try {
            MessageDigest md = MessageDigest.getInstance("SHA-256");
            byte[] hash = md.digest(password.getBytes(StandardCharsets.UTF_8));
            BigInteger number = new BigInteger(1, hash);
            StringBuilder hexString = new StringBuilder(number.toString(16));
            while (hexString.length() < 32) {
                hexString.insert(0, '0');
            }
            return hexString.toString();
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        }
    }
}
        
        

The updated code now includes a password hashing mechanism. The password is no longer stored in plain text in the source code. Instead, the SHA-256 hashing algorithm is used to create a hashed version of the password. This hashed password is then stored and used for comparison during the authentication process.

The hashPassword method is a helper method that takes a password string as input and returns the hashed version of the password. It uses the MessageDigest class from the java.security package to create a MessageDigest instance for the SHA-256 algorithm. The password string is then converted to bytes and passed to the digest method of the MessageDigest instance to compute the hash. The resulting byte array is then converted to a BigInteger and then to a hexadecimal string.

During the authentication process, the entered password is hashed using the same hashPassword method and compared with the stored hashed password. If the hashed passwords match, the authentication is successful.

This approach ensures that even if an attacker gains access to the source code, they will not be able to retrieve the original password from the hashed password. This significantly improves the security of the application.

References