Insecure object reference - Financial information - Java

Insecure object reference - Financial information - Java

Need

Secure access control for financial information

Context

  • Usage of Java 8 for developing applications with enhanced features and performance
  • Usage of HttpServletRequest for handling HTTP requests in Java Servlets
  • Usage of HttpServletResponse for handling HTTP responses in Java Servlets
  • Usage of javax.crypto.Cipher for encryption and decryption operations
  • Usage of java.util.Base64 for encoding and decoding data in Base64 format
  • Usage of java.io.IOException for handling input/output errors in Java
  • Usage of java.util.List for storing and manipulating collections of objects in Java
  • Usage of javax.servlet.RequestDispatcher for handling requests and dispatching them to different resources in a Java Servlet application.
  • Usage of StandardCharsets from java.nio.charset for handling character encodings in Java
  • Usage of javax.servlet.ServletException for handling servlet exceptions

Description

Non compliant code

        import javax.servlet.http.*;
import javax.crypto.Cipher;
import java.util.Base64;

public class CreditCardController extends HttpServlet {

    public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String encryptedCardNumber = request.getParameter("cardNumber");
        String decryptedCardNumber = decryptCardNumber(encryptedCardNumber);
        CreditCard card = CreditCardService.getCardByNumber(decryptedCardNumber);
        List transactions = TransactionService.getTransactionsForCard(card);
        request.setAttribute("transactions", transactions);
        RequestDispatcher dispatcher = request.getRequestDispatcher("/WEB-INF/views/transactions.jsp");
        dispatcher.forward(request, response);
    }

    private String decryptCardNumber(String encryptedCardNumber) {
        try {
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec);
            byte[] decodedBytes = Base64.getDecoder().decode(encryptedCardNumber);
            byte[] decryptedBytes = cipher.doFinal(decodedBytes);
            return new String(decryptedBytes, StandardCharsets.UTF_8);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}
        
        

The above code represents a Servlet which handles HTTP GET requests to fetch transaction history for a given credit card number.

The 'doGet' method retrieves the encrypted card number from the request parameters and decrypts it using the 'decryptCardNumber' method. The decrypted card number is then used to fetch the CreditCard object from the CreditCardService. Once the card object is retrieved, the TransactionService is used to fetch all transactions associated with the card. These transactions are then set as an attribute on the request and forwarded to a JSP page for display.

The vulnerability here lies in the fact that there is no validation to check if the account making the request is the one associated with the credit card. This means that a user can potentially make a request for transaction history of a card that they do not own, leading to unauthorized access to sensitive financial information. This is an example of an Insecure Direct Object Reference (IDOR) vulnerability.

Steps

  • Implement proper authentication and authorization mechanisms to ensure that only authorized users can access the credit card information.
  • Ensure that the credit card information is encrypted and securely stored in the database.
  • Implement access controls to restrict access to the credit card information based on the user's session and associated account.
  • Validate the user's session and ensure that the account associated with the credit card matches the account from which the request is being made.
  • Implement proper input validation and sanitization to prevent any malicious input from being processed.
  • Regularly update and patch the encryption algorithms and libraries used for encrypting and decrypting the credit card information.
  • Conduct regular security audits and penetration testing to identify and fix any vulnerabilities in the system.
  • Train developers and staff on secure coding practices and the importance of protecting sensitive financial information.

Compliant code

        import javax.servlet.http.*;
import javax.crypto.Cipher;
import java.util.Base64;

public class CreditCardController extends HttpServlet {

    public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        HttpSession session = request.getSession();
        User user = (User) session.getAttribute("user");
        
        String encryptedCardNumber = request.getParameter("cardNumber");
        String decryptedCardNumber = decryptCardNumber(encryptedCardNumber);
        
        CreditCard card = CreditCardService.getCardByNumber(decryptedCardNumber);
        
        if (card == null || !card.getUser().equals(user)) {
            response.sendError(HttpServletResponse.SC_FORBIDDEN);
            return;
        }
        
        List transactions = TransactionService.getTransactionsForCard(card);
        request.setAttribute("transactions", transactions);
        RequestDispatcher dispatcher = request.getRequestDispatcher("/WEB-INF/views/transactions.jsp");
        dispatcher.forward(request, response);
    }

    private String decryptCardNumber(String encryptedCardNumber) {
        try {
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec);
            byte[] decodedBytes = Base64.getDecoder().decode(encryptedCardNumber);
            byte[] decryptedBytes = cipher.doFinal(decodedBytes);
            return new String(decryptedBytes, StandardCharsets.UTF_8);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}
        
        

The updated code now includes a check to ensure that the user making the request is the owner of the credit card. This is done by retrieving the user from the session and comparing it with the user associated with the credit card. If the users do not match, a 403 Forbidden error is returned.

The doGet method now starts by retrieving the HttpSession and the User object from it. This User object should be set when the user logs in. Then, it retrieves the encrypted card number from the request parameters and decrypts it.

Next, it retrieves the CreditCard object associated with the decrypted card number. If no such card exists or if the user associated with the card does not match the user from the session, it sends a 403 Forbidden error and returns.

If the user is authorized, it retrieves the transactions associated with the card, sets them as a request attribute, and forwards the request to the transactions view.

The decryptCardNumber method remains unchanged. It decrypts the encrypted card number using AES encryption with CBC mode and PKCS5 padding. If an error occurs during decryption, it throws a RuntimeException.

This code ensures that only the authorized user can access the credit card information, thus fixing the insecure direct object reference vulnerability.

References