Skip to main content Link Menu Expand (external link) Document Search Copy Copied

Java

Table of contents

  1. Exposure of sensitive information
  2. Insertion of Sensitive Information Into Sent Data
  3. Cross-Site Request Forgery (CSRF)
  4. Use of Hard-coded Password
  5. Broken or Risky Crypto Algorithm
  6. Insufficient Entropy
  7. XSS
  8. SQL Injection
  9. External Control of File Name or Path
  10. Generation of Error Message Containing Sensitive Information
  11. unprotected storage of credentials
  12. Trust Boundary Violation
  13. Insufficiently Protected Credentials
  14. Restriction of XML External Entity Reference
  15. Vulnerable and Outdated Components
  16. Improper Validation of Certificate with Host Mismatch
  17. Improper Authentication
  18. Session Fixation
  19. Inclusion of Functionality from Untrusted Control
  20. Download of Code Without Integrity Check
  21. Deserialization of Untrusted Data
  22. Insufficient Logging
  23. Improper Output Neutralization for Logs
  24. Omission of Security-relevant Information
  25. Sensitive Information into Log File
  26. Server-Side Request Forgery (SSRF)

Exposure of sensitive information

Noncompliant code:

import java.util.logging.*;

public class UserController {
    private static final Logger LOGGER = Logger.getLogger(UserController.class.getName());

    public void loginUser(String username, String password) {
        // Perform login logic

        LOGGER.info("User logged in - username: " + username);
    }
}

In this noncompliant code, the loginUser method logs the username of the user who successfully logged in using the LOGGER.info statement. However, logging sensitive information like usernames can be risky because the log files might be accessible to unauthorized users or stored insecurely, leading to potential exposure of sensitive data.

To address this issue, here’s an example of compliant code that avoids exposing sensitive information via logs:

Compliant code:

import java.util.logging.*;

public class UserController {
    private static final Logger LOGGER = Logger.getLogger(UserController.class.getName());

    public void loginUser(String username, String password) {
        // Perform login logic

        LOGGER.info("User logged in - username: " + obfuscateUsername(username));
    }

    private String obfuscateUsername(String username) {
        // Implement a method to obfuscate or mask the username
        // Example: Replace characters with asterisks or hash the username
        // ...

        return username; // Return the obfuscated username
    }
}

In the compliant code, the loginUser method no longer directly logs the username. Instead, it calls the obfuscateUsername method, which obfuscates or masks the sensitive information before it is logged. This can be done by replacing characters with asterisks, hashing the username, or using other appropriate obfuscation techniques.

By obfuscating the sensitive information in the logs, the compliant code helps protect the confidentiality of the data, even if the log files are exposed or accessed by unauthorized individuals.

Insertion of Sensitive Information Into Sent Data

Noncompliant code:

import java.net.HttpURLConnection;
import java.net.URL;
import java.io.OutputStream;
import java.io.IOException;

public class PaymentService {
    private static final String API_ENDPOINT = "https://api.example.com/payments";

    public void makePayment(String cardNumber, double amount) {
        try {
            // Create a connection to the API endpoint
            URL url = new URL(API_ENDPOINT);
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            connection.setRequestMethod("POST");

            // Set the request headers
            connection.setRequestProperty("Content-Type", "application/json");

            // Construct the request body
            String requestBody = "{\"cardNumber\": \"" + cardNumber + "\", \"amount\": " + amount + "}";

            // Send the request
            connection.setDoOutput(true);
            OutputStream outputStream = connection.getOutputStream();
            outputStream.write(requestBody.getBytes());
            outputStream.flush();
            outputStream.close();

            // Process the response...
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

In this noncompliant code, the makePayment method accepts the cardNumber and amount as parameters and constructs the request body directly by concatenating the sensitive information into the JSON string. This approach is insecure because it exposes the sensitive information (in this case, the card number) in clear text, which could be intercepted or logged by attackers.

To address this issue, here’s an example of compliant code that properly handles sensitive information in sent data:

Compliant code:

import java.net.HttpURLConnection;
import java.net.URL;
import java.io.OutputStream;
import java.io.IOException;

public class PaymentService {
    private static final String API_ENDPOINT = "https://api.example.com/payments";

    public void makePayment(String cardNumber, double amount) {
        try {
            // Create a connection to the API endpoint
            URL url = new URL(API_ENDPOINT);
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            connection.setRequestMethod("POST");

            // Set the request headers
            connection.setRequestProperty("Content-Type", "application/json");

            // Construct the request body using a JSON library or object mapping
            JsonObject requestBody = new JsonObject();
            requestBody.addProperty("cardNumber", obfuscateCardNumber(cardNumber));
            requestBody.addProperty("amount", amount);

            // Send the request
            connection.setDoOutput(true);
            OutputStream outputStream = connection.getOutputStream();
            outputStream.write(requestBody.toString().getBytes());
            outputStream.flush();
            outputStream.close();

            // Process the response...
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private String obfuscateCardNumber(String cardNumber) {
        // Implement a method to obfuscate or mask the card number
        // Example: Replace characters with asterisks, mask certain digits, or encrypt the card number
        // ...

        return cardNumber; // Return the obfuscated card number
    }
}

In the compliant code, the makePayment method no longer directly inserts the sensitive information into the request body string. Instead, it uses a JSON library or object mapping technique to construct the request body. The sensitive information, such as the cardNumber, is passed through the obfuscateCardNumber method, which performs appropriate obfuscation or masking techniques to protect the data before it is included in the request body.

By properly handling the sensitive information and obfuscating it before sending, the compliant code helps protect the confidentiality of the data during transmission, reducing the risk of unauthorized access or interception.

Cross-Site Request Forgery (CSRF)

Noncompliant code:

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class AccountService {
    public void updateEmail(HttpServletRequest request, HttpServletResponse response) {
        String newEmail = request.getParameter("email");

        // Code to update the email address in the user's account...
        // ...
    }
}

In this noncompliant code, the updateEmail method is susceptible to CSRF attacks because it doesn’t include any protection against such attacks. An attacker can craft a malicious web page or form that includes a hidden field containing the request to update the email address. When an unsuspecting user visits this malicious page while authenticated in the target application, their browser automatically sends the request to the updateEmail endpoint, resulting in an unauthorized email address update.

To address this issue, here’s an example of compliant code that implements CSRF protection measures:

Compliant code:

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.util.UUID;

public class AccountService {
    private static final String CSRF_TOKEN_SESSION_ATTR = "csrfToken";

    public void updateEmail(HttpServletRequest request, HttpServletResponse response) {
        String newEmail = request.getParameter("email");

        // Validate CSRF token
        HttpSession session = request.getSession();
        String csrfToken = (String) session.getAttribute(CSRF_TOKEN_SESSION_ATTR);
        String requestCsrfToken = request.getParameter("csrfToken");

        if (csrfToken == null || !csrfToken.equals(requestCsrfToken)) {
            response.setStatus(HttpServletResponse.SC_FORBIDDEN);
            return;
        }

        // Code to update the email address in the user's account...
        // ...
    }

    public void generateCsrfToken(HttpServletRequest request) {
        HttpSession session = request.getSession();
        String csrfToken = UUID.randomUUID().toString();
        session.setAttribute(CSRF_TOKEN_SESSION_ATTR, csrfToken);
    }
}

In the compliant code, several measures are implemented to prevent CSRF attacks.

  1. The updateEmail method retrieves the CSRF token from both the session and the request parameters. It compares the two tokens to ensure they match. If the tokens don’t match or if the CSRF token is missing, the method returns a forbidden status, preventing the unauthorized update.

  2. The generateCsrfToken method generates a unique CSRF token using a UUID and stores it in the user’s session. This method is called when rendering the form or page that requires CSRF protection. The generated token should be included as a hidden field in the form.

By including and validating the CSRF token in requests, the compliant code protects against CSRF attacks, ensuring that requests to sensitive actions are only accepted from legitimate sources and preventing unauthorized actions from being performed on behalf of authenticated users.

Use of Hard-coded Password

Noncompliant code:

public class DatabaseConnection {
    private static final String DB_URL = "jdbc:mysql://localhost:3306/mydatabase";
    private static final String DB_USERNAME = "root";
    private static final String DB_PASSWORD = "password123";

    public void connect() {
        // Code to establish a database connection using the hard-coded credentials
        // ...
    }
}

In this noncompliant code, the database connection information, including the password, is hard-coded directly into the code. This practice is highly insecure because if an attacker gains access to the source code or decompiles the application, they can easily retrieve the password and potentially compromise the database.

To address this issue, here’s an example of compliant code that avoids hard-coding passwords:

Compliant code:

public class DatabaseConnection {
    private static final String DB_URL = "jdbc:mysql://localhost:3306/mydatabase";
    private static final String DB_USERNAME = "root";
    private String dbPassword;

    public DatabaseConnection(String dbPassword) {
        this.dbPassword = dbPassword;
    }

    public void connect() {
        // Code to establish a database connection using the provided password
        // ...
    }
}

In the compliant code, the hard-coded password is replaced with a constructor parameter dbPassword. The password is no longer stored directly in the code but is instead passed as an argument when creating an instance of the DatabaseConnection class. This allows the password to be provided securely at runtime, such as through a configuration file or environment variable.

By avoiding the use of hard-coded passwords and storing them securely, the compliant code reduces the risk of unauthorized access to sensitive information, such as database credentials, in case of a code compromise or unauthorized access to the source code.

Broken or Risky Crypto Algorithm

Noncompliant code:

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

public class PasswordUtils {
    public static String hashPassword(String password) {
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            byte[] hash = md.digest(password.getBytes());
            StringBuilder sb = new StringBuilder();
            for (byte b : hash) {
                sb.append(String.format("%02x", b));
            }
            return sb.toString();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return null;
    }
}

In this noncompliant code, the hashPassword method uses the MD5 algorithm to hash the provided password. MD5 is considered broken and insecure for password hashing because it is susceptible to various attacks, such as collision attacks and preimage attacks. It is no longer recommended for cryptographic purposes.

To address this issue, here’s an example of compliant code that uses a more secure cryptographic algorithm, such as bcrypt:

Compliant code:

import org.mindrot.jbcrypt.BCrypt;

public class PasswordUtils {
    private static final int BCRYPT_COST = 12;

    public static String hashPassword(String password) {
        return BCrypt.hashpw(password, BCrypt.gensalt(BCRYPT_COST));
    }

    public static boolean verifyPassword(String password, String hashedPassword) {
        return BCrypt.checkpw(password, hashedPassword);
    }
}

In the compliant code, the hashPassword method uses the bcrypt algorithm, which is a widely accepted and secure cryptographic algorithm for password hashing. It generates a salt and incorporates a cost factor to slow down the hashing process, making it computationally expensive for attackers to perform brute-force attacks. The verifyPassword method is also provided to verify the password against the stored hashed password.

By using a secure cryptographic algorithm like bcrypt instead of broken or risky ones, the compliant code improves the overall security of password storage and helps protect user credentials from unauthorized access.

Insufficient Entropy

Noncompliant code:

import java.util.Random;

public class TokenGenerator {
    public static String generateToken(int length) {
        String characters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
        StringBuilder sb = new StringBuilder();
        Random random = new Random();
        for (int i = 0; i < length; i++) {
            int index = random.nextInt(characters.length());
            char c = characters.charAt(index);
            sb.append(c);
        }
        return sb.toString();
    }
}

In this noncompliant code, the generateToken method generates a token of a specified length using a random selection of characters from the characters string. However, the randomness of the generated token is insufficient. It relies on the java.util.Random class, which uses a predictable algorithm and may produce values with low entropy. This can make the generated tokens more susceptible to brute-force attacks or guessability.

To address this issue, here’s an example of compliant code that uses a more secure approach for generating tokens with sufficient entropy:

Compliant code:

import java.security.SecureRandom;
import java.util.Base64;

public class TokenGenerator {
    public static String generateToken(int length) {
        byte[] bytes = new byte[length];
        SecureRandom secureRandom = new SecureRandom();
        secureRandom.nextBytes(bytes);
        return Base64.getUrlEncoder().withoutPadding().encodeToString(bytes);
    }
}

In the compliant code, the generateToken method uses java.security.SecureRandom to generate a cryptographically secure random byte array of the specified length. The SecureRandom class provides a higher level of entropy compared to java.util.Random, making the generated tokens more unpredictable. The resulting byte array is then encoded using Base64 URL encoding to produce a token string.

By using a cryptographically secure random number generator and ensuring sufficient entropy in the generated tokens, the compliant code improves the security of the token generation process and reduces the risk of token guessing or brute-force attacks.

XSS

Noncompliant code:

public class XssExample {
    public static String getUserInput() {
        // Assume user input is obtained from an untrusted source
        String userInput = "<script>alert('XSS');</script>";
        return userInput;
    }
    
    public static String displayUserInput(String userInput) {
        String html = "<div>" + userInput + "</div>";
        return html;
    }
    
    public static void main(String[] args) {
        String userInput = getUserInput();
        String html = displayUserInput(userInput);
        System.out.println(html);
    }
}

In this noncompliant code, the getUserInput method simulates user input obtained from an untrusted source. The input contains a malicious script tag that tries to execute an alert box. The displayUserInput method simply wraps the user input in an HTML div element.

When the main method is executed, the malicious script tag is rendered as-is in the output, potentially causing a cross-site scripting vulnerability. If this output is displayed in a web page, the script will be executed in the user’s browser, leading to unwanted behavior.

To address this XSS vulnerability, here’s an example of compliant code that properly sanitizes the user input:

Compliant code:

import org.apache.commons.text.StringEscapeUtils;

public class XssExample {
    public static String getUserInput() {
        // Assume user input is obtained from an untrusted source
        String userInput = "<script>alert('XSS');</script>";
        return userInput;
    }
    
    public static String displayUserInput(String userInput) {
        String sanitizedInput = StringEscapeUtils.escapeHtml4(userInput);
        String html = "<div>" + sanitizedInput + "</div>";
        return html;
    }
    
    public static void main(String[] args) {
        String userInput = getUserInput();
        String html = displayUserInput(userInput);
        System.out.println(html);
    }
}

In the compliant code, the StringEscapeUtils.escapeHtml4 method from Apache Commons Text library is used to properly escape the user input. This method replaces characters with their corresponding HTML entities, preventing the script from being executed as code. The sanitized input is then safely rendered within the HTML div element.

By properly sanitizing user input and escaping special characters, the compliant code prevents the execution of malicious scripts and mitigates the risk of cross-site scripting attacks.

SQL Injection

Noncompliant code:

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;

public class SqlInjectionExample {
    public static void main(String[] args) {
        String username = "admin'; DROP TABLE users;--";
        String password = "password";
        
        String query = "SELECT * FROM users WHERE username='" + username + "' AND password='" + password + "'";
        
        try {
            Connection connection = Database.getConnection();
            Statement statement = connection.createStatement();
            ResultSet resultSet = statement.executeQuery(query);
            
            // Process the result set...
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

In this noncompliant code, the SQL query is constructed by directly concatenating user-supplied input (username and password) into the query string. The username value is intentionally crafted to include a malicious SQL statement that attempts to drop the users table. This leaves the application vulnerable to SQL injection attacks.

To address this SQL injection vulnerability, here’s an example of compliant code that uses prepared statements and parameterized queries to mitigate the risk:

Compliant code:

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;

public class SqlInjectionExample {
    public static void main(String[] args) {
        String username = "admin'; DROP TABLE users;--";
        String password = "password";
        
        String query = "SELECT * FROM users WHERE username=? AND password=?";
        
        try {
            Connection connection = Database.getConnection();
            PreparedStatement statement = connection.prepareStatement(query);
            statement.setString(1, username);
            statement.setString(2, password);
            
            ResultSet resultSet = statement.executeQuery();
            
            // Process the result set...
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

In the compliant code, the SQL query is parameterized using placeholders (?) for the user-supplied values. The values are then bound to the prepared statement using the setString method. By using prepared statements, the SQL query is precompiled and the user input is treated as data rather than executable SQL code. This effectively prevents SQL injection attacks by ensuring that user input is properly escaped and not interpreted as part of the SQL syntax.

By adopting prepared statements and parameterized queries, the compliant code mitigates the risk of SQL injection vulnerabilities and ensures the safe execution of database queries.

External Control of File Name or Path

Noncompliant code:

import java.io.File;

public class FileUploadExample {
    public static void main(String[] args) {
        String fileName = getFileNameFromUserInput();
        String directory = "uploads/";

        File file = new File(directory + fileName);
        
        // Process the uploaded file...
    }
    
    private static String getFileNameFromUserInput() {
        // Code to get file name from user input
        // This could be from a user input field, request parameter, etc.
        return userInput;
    }
}

In this noncompliant code, the fileName variable is obtained from user input without proper validation or sanitization. The user can potentially manipulate the file name to access files outside the intended directory, leading to unauthorized access or information disclosure.

To address this vulnerability, here’s an example of compliant code that validates and sanitizes the file name before constructing the file path:

Compliant code:

import java.io.File;
import java.nio.file.Path;
import java.nio.file.Paths;

public class FileUploadExample {
    private static final String UPLOAD_DIRECTORY = "uploads/";

    public static void main(String[] args) {
        String fileName = getFileNameFromUserInput();
        
        Path filePath = Paths.get(UPLOAD_DIRECTORY, fileName).normalize();
        if (!filePath.startsWith(UPLOAD_DIRECTORY)) {
            // Invalid file name or path, handle the error
            return;
        }

        File file = filePath.toFile();
        
        // Process the uploaded file...
    }
    
    private static String getFileNameFromUserInput() {
        // Code to get file name from user input
        // This could be from a user input field, request parameter, etc.
        return userInput;
    }
}

In the compliant code, the file name obtained from user input is validated and sanitized before constructing the file path. The Paths.get() method is used to create a Path object, and the normalize() method is applied to ensure a consistent and secure representation of the file path. The startsWith() method is then used to verify that the resulting file path is within the intended upload directory. If the file path is determined to be invalid or outside the designated directory, appropriate error handling can be performed.

By validating and sanitizing the file name, and properly constructing the file path, the compliant code mitigates the risk of external control of file names or paths and helps ensure that only authorized files are accessed or processed.

Generation of Error Message Containing Sensitive Information

Noncompliant code:

public class UserService {
    public User getUserById(String userId) {
        try {
            // Code to fetch user details from the database using the provided userId
            // ...
        } catch (Exception e) {
            String errorMessage = "An error occurred while fetching user details for userId: " + userId;
            throw new RuntimeException(errorMessage, e);
        }
    }
}

In this noncompliant code, an error message is constructed by concatenating the sensitive information (the userId parameter) with a generic error message. This can potentially expose the sensitive information to unauthorized individuals in case of an error or exception.

To address this vulnerability, here’s an example of compliant code that avoids exposing sensitive information in error messages:

Compliant code:

public class UserService {
    public User getUserById(String userId) {
        try {
            // Code to fetch user details from the database using the provided userId
            // ...
        } catch (Exception e) {
            throw new RuntimeException("An error occurred while fetching user details", e);
        }
    }
}

In the compliant code, the error message is kept generic and does not include any sensitive information. By removing the sensitive data from the error message, the compliant code helps to protect the confidentiality of the user information and reduces the risk of exposing sensitive information to potential attackers.

unprotected storage of credentials

Noncompliant code:

public class UserService {
    private String username;
    private String password;
    
    public void login(String username, String password) {
        this.username = username;
        this.password = password;
        // Code to authenticate the user
        // ...
    }
    
    public void printCredentials() {
        System.out.println("Username: " + username);
        System.out.println("Password: " + password);
    }
}

In this noncompliant code, the username and password fields are stored as plain strings within the UserService class. The credentials are directly assigned from the login method and can be accessed and printed using the printCredentials method. Storing credentials in this manner poses a security risk as they can easily be accessed and exposed.

To address this vulnerability, here’s an example of compliant code that implements protected storage of credentials:

Compliant code:

public class UserService {
    private char[] password;
    
    public void login(String username, char[] password) {
        // Code to authenticate the user
        // ...
        
        // Store the password securely
        this.password = Arrays.copyOf(password, password.length);
        
        // Clear the original password data
        Arrays.fill(password, ' ');
    }
    
    public void printCredentials() {
        System.out.println("Username: " + getUsername());
        System.out.println("Password: ********");
    }
    
    private String getUsername() {
        // Retrieve the username from the authenticated user session
        // ...
    }
}

In the compliant code, the password is stored as a character array (char[]) instead of a plain string. Storing the password as a character array allows for more secure handling as it can be cleared from memory once it is no longer needed. Additionally, the printCredentials method only displays the username while masking the password with asterisks to prevent inadvertent exposure.

By implementing protected storage of credentials, the compliant code mitigates the risk of exposing sensitive information and enhances the overall security of the application.

Trust Boundary Violation

Noncompliant code:

public class UserAuthenticator {
    private boolean isAdmin;
    
    public boolean authenticateUser(String username, String password) {
        // Code to authenticate the user credentials
        // ...
        
        // Set isAdmin flag based on the authentication result
        if (username.equals("admin") && password.equals("admin123")) {
            isAdmin = true;
        }
        
        return true;
    }
    
    public void performAdminAction() {
        if (isAdmin) {
            // Code to perform administrative action
            // ...
        } else {
            System.out.println("Access denied. You are not authorized to perform this action.");
        }
    }
}

In this noncompliant code, the UserAuthenticator class authenticates a user based on the provided credentials (username and password). If the authentication is successful for an admin user (hard-coded as “admin” and “admin123” in this example), the isAdmin flag is set to true. The performAdminAction method checks the isAdmin flag to determine whether the user is authorized to perform an administrative action.

The trust boundary violation occurs because the UserAuthenticator class allows the isAdmin flag to be manipulated from outside the authentication process. An attacker could potentially modify the isAdmin flag directly or through other means, bypassing the proper authentication process and gaining unauthorized access to perform administrative actions.

To address this vulnerability, here’s an example of compliant code that enforces the trust boundary properly:

Compliant code:

public class UserAuthenticator {
    private boolean isAdmin;
    
    public boolean authenticateUser(String username, String password) {
        // Code to authenticate the user credentials
        // ...
        
        // Set isAdmin flag based on the authentication result
        if (username.equals("admin") && password.equals("admin123")) {
            isAdmin = true;
        } else {
            isAdmin = false;
        }
        
        return true;
    }
    
    public void performAdminAction() {
        if (checkAdminStatus()) {
            // Code to perform administrative action
            // ...
        } else {
            System.out.println("Access denied. You are not authorized to perform this action.");
        }
    }
    
    private boolean checkAdminStatus() {
        // Code to check the isAdmin flag from the authenticated user session
        // ...
        
        return isAdmin;
    }
}

In the compliant code, the isAdmin flag is properly enforced within the UserAuthenticator class. The flag is set during the authentication process based on the result of validating the user’s credentials. The performAdminAction method calls the checkAdminStatus method, which internally checks the isAdmin flag from the authenticated user session.

By enforcing the trust boundary correctly, the compliant code ensures that only authenticated users with legitimate admin privileges can perform administrative actions. This prevents unauthorized access and strengthens the security of the application.

Insufficiently Protected Credentials

Noncompliant code:

public class UserAuthenticator {
    public boolean authenticateUser(String username, String password) {
        // Code to authenticate the user credentials
        // ...
        
        // Log the username and password
        System.out.println("User credentials: " + username + ", " + password);
        
        // Continue with authentication logic
        // ...
        
        return true;
    }
}

In this noncompliant code, the UserAuthenticator class contains a method authenticateUser that takes the username and password as parameters for user authentication. However, the code lacks proper protection for the sensitive credentials. The System.out.println statement logs the credentials directly to the console, exposing them to potential attackers or unauthorized individuals who might have access to the log files.

To address this vulnerability, here’s an example of compliant code that properly protects the credentials:

Compliant code:

public class UserAuthenticator {
    public boolean authenticateUser(String username, String password) {
        // Code to authenticate the user credentials
        // ...
        
        // Log a generic message instead of the credentials
        System.out.println("User authentication attempt");
        
        // Continue with authentication logic
        // ...
        
        return true;
    }
}

In the compliant code, the System.out.println statement has been modified to log a generic message instead of the actual credentials. By avoiding the direct logging of sensitive information, such as usernames and passwords, the compliant code reduces the risk of exposing sensitive credentials to unauthorized individuals or potential attackers.

It’s important to note that in a production environment, logging sensitive information like passwords should generally be avoided altogether. Instead, consider using proper logging frameworks that support sensitive data protection mechanisms, such as redaction or encryption, to ensure the confidentiality of sensitive information.

Restriction of XML External Entity Reference

Noncompliant code:

import org.w3c.dom.Document;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.ByteArrayInputStream;

public class XMLParser {
    public Document parseXML(String xml) {
        try {
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            DocumentBuilder builder = factory.newDocumentBuilder();
            Document document = builder.parse(new ByteArrayInputStream(xml.getBytes()));
            return document;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}

In this noncompliant code, the XMLParser class contains a method parseXML that takes an XML string as input and parses it into a Document object using the javax.xml.parsers.DocumentBuilder class. However, the code does not properly restrict XML external entity references, which can lead to security vulnerabilities like XXE attacks.

To address this vulnerability, here’s an example of compliant code that implements proper restriction of XML external entity references:

Compliant code:

import org.w3c.dom.Document;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.ByteArrayInputStream;

public class XMLParser {
    public Document parseXML(String xml) {
        try {
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
            factory.setFeature("http://xml.org/sax/features/external-general-entities", false);
            factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
            DocumentBuilder builder = factory.newDocumentBuilder();
            Document document = builder.parse(new ByteArrayInputStream(xml.getBytes()));
            return document;
        } catch (ParserConfigurationException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}

In the compliant code, the DocumentBuilderFactory is configured to disable the support for document type declarations (DTDs) and external entity references by setting the corresponding features. By disabling these features, the code effectively restricts XML external entity references and prevents potential XXE attacks.

It’s crucial to be cautious when parsing XML data and to properly restrict XML external entity references to mitigate the risk of XXE vulnerabilities.

Vulnerable and Outdated Components

Noncompliant code:

import org.apache.commons.lang.StringUtils;

public class StringHelper {
    public static String sanitizeString(String input) {
        return StringUtils.stripTags(input);
    }

    public static boolean isNullOrEmpty(String input) {
        return StringUtils.isEmpty(input);
    }

    public static boolean isNumeric(String input) {
        return StringUtils.isNumeric(input);
    }
}

In this noncompliant code, the StringHelper class uses the StringUtils class from the Apache Commons Lang library to perform string manipulation and validation. However, the code uses an outdated version of the library that may have known vulnerabilities.

To address this issue, it is important to keep all software components, including third-party libraries, up to date. Here’s an example of compliant code that uses an updated version of the library:

Compliant code:

import org.apache.commons.lang3.StringUtils;

public class StringHelper {
    public static String sanitizeString(String input) {
        return StringUtils.stripTags(input);
    }

    public static boolean isNullOrEmpty(String input) {
        return StringUtils.isEmpty(input);
    }

    public static boolean isNumeric(String input) {
        return StringUtils.isNumeric(input);
    }
}

In the compliant code, the StringUtils class is imported from the org.apache.commons.lang3 package, indicating the use of the latest version of the Apache Commons Lang library (version 3.x). By using an updated version of the library, the code mitigates the risk of known vulnerabilities present in older versions.

It is crucial to regularly update software components, especially third-party libraries, to ensure the use of the latest security patches and fixes. Keeping components up to date helps protect against known vulnerabilities and ensures a more secure application.

Improper Validation of Certificate with Host Mismatch

Noncompliant code:

import javax.net.ssl.HttpsURLConnection;
import java.io.IOException;
import java.net.URL;

public class HttpClient {
    public static void sendRequest(String url) throws IOException {
        URL requestUrl = new URL(url);
        HttpsURLConnection connection = (HttpsURLConnection) requestUrl.openConnection();
        connection.setHostnameVerifier((hostname, session) -> true); // Disabling hostname verification
        connection.setRequestMethod("GET");
        int responseCode = connection.getResponseCode();
        // Process the response...
    }
}

In this noncompliant code, the sendRequest method sends an HTTP GET request to the specified URL. However, the code disables hostname verification by setting a custom HostnameVerifier that always returns true. This means that the certificate presented by the server is not properly validated against the host name of the URL. This opens up the possibility of man-in-the-middle attacks and exposes the application to security risks.

To address this issue, it is essential to perform proper validation of the certificate with the host name of the URL. Here’s an example of compliant code that implements proper certificate validation:

Compliant code:

import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLPeerUnverifiedException;
import javax.net.ssl.SSLSession;
import java.io.IOException;
import java.net.URL;

public class HttpClient {
    public static void sendRequest(String url) throws IOException {
        URL requestUrl = new URL(url);
        HttpsURLConnection connection = (HttpsURLConnection) requestUrl.openConnection();
        connection.setRequestMethod("GET");
        try {
            connection.connect();
            SSLSession session = connection.getSSLSession();
            String peerHost = session.getPeerHost();
            if (!requestUrl.getHost().equals(peerHost)) {
                throw new SSLPeerUnverifiedException("Certificate does not match the host name");
            }
        } catch (SSLPeerUnverifiedException e) {
            // Handle certificate validation failure
        } finally {
            connection.disconnect();
        }
        int responseCode = connection.getResponseCode();
        // Process the response...
    }
}

In the compliant code, the sendRequest method establishes an HTTPS connection and performs proper certificate validation. It compares the host name of the URL with the host name obtained from the SSL session’s peer. If there is a mismatch, it throws an SSLPeerUnverifiedException to indicate that the certificate does not match the host name.

By implementing proper certificate validation, the code ensures that the certificate presented by the server is validated against the host name of the URL, reducing the risk of man-in-the-middle attacks and enhancing the overall security of the application.

Improper Authentication

Noncompliant code:

import java.util.Scanner;

public class AuthenticationExample {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        
        System.out.print("Enter username: ");
        String username = scanner.nextLine();
        
        System.out.print("Enter password: ");
        String password = scanner.nextLine();
        
        if (username.equals("admin") && password.equals("password")) {
            System.out.println("Authentication successful");
            // Proceed with privileged operation
        } else {
            System.out.println("Authentication failed");
            // Handle authentication failure
        }
    }
}

In this noncompliant code, the username and password are collected from user input using a Scanner object. However, there is no proper mechanism in place to securely store and compare the credentials. The username and password are compared using simple string equality, which is vulnerable to various attacks such as brute-force attacks, dictionary attacks, and interception of the credentials.

To address this issue, here’s a compliant code example:

Compliant code:

import java.util.Scanner;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

public class AuthenticationExample {
    private static final String SALT = "random_salt";
    
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        
        System.out.print("Enter username: ");
        String username = scanner.nextLine();
        
        System.out.print("Enter password: ");
        String password = scanner.nextLine();
        
        if (authenticate(username, password)) {
            System.out.println("Authentication successful");
            // Proceed with privileged operation
        } else {
            System.out.println("Authentication failed");
            // Handle authentication failure
        }
    }
    
    private static boolean authenticate(String username, String password) {
        // Retrieve hashed password from a secure database or storage
        String storedPasswordHash = getStoredPasswordHash(username);
        
        // Hash the input password with a salt
        String hashedPassword = hashPassword(password);
        
        // Compare the stored hashed password with the input hashed password
        return storedPasswordHash.equals(hashedPassword);
    }
    
    private static String hashPassword(String password) {
        try {
            MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
            messageDigest.update((password + SALT).getBytes());
            byte[] hashedBytes = messageDigest.digest();
            return bytesToHexString(hashedBytes);
        } catch (NoSuchAlgorithmException e) {
            // Handle the exception
            e.printStackTrace();
        }
        return null;
    }
    
    private static String bytesToHexString(byte[] bytes) {
        StringBuilder stringBuilder = new StringBuilder();
        for (byte b : bytes) {
            stringBuilder.append(String.format("%02x", b));
        }
        return stringBuilder.toString();
    }
    
    private static String getStoredPasswordHash(String username) {
        // Retrieve the hashed password from a secure database or storage
        // based on the given username
        // Return the stored password hash
        return "stored_password_hash";
    }
}

In this compliant code, the password is securely hashed using a strong cryptographic hash function (SHA-256) with the addition of a salt value. The hashed password is then compared with the stored hashed password retrieved from a secure database or storage. This approach enhances the security of the authentication process by preventing the exposure of plain-text passwords and protecting against common attack vectors such as brute-force and dictionary attacks.

Session Fixation

Noncompliant code:

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

public class SessionFixationExample {
    public static void login(HttpServletRequest request, String username) {
        HttpSession session = request.getSession(true);
        session.setAttribute("username", username);
    }
    
    public static void main(String[] args) {
        HttpServletRequest request = // Obtain the request object
        
        String username = "admin";
        login(request, username);
        
        // Proceed with authenticated actions
    }
}

In this noncompliant code, the login method is called to authenticate a user and create a new session. However, the login method does not perform any session management or regeneration. It simply sets the username attribute in the session. This creates a vulnerability known as session fixation, where an attacker can force a victim’s session identifier to a known value and then later hijack the session.

To address this issue, here’s a compliant code example:

Compliant code:

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

public class SessionFixationExample {
    public static void login(HttpServletRequest request, String username) {
        HttpSession session = request.getSession();
        session.invalidate(); // Invalidate the existing session
        session = request.getSession(true); // Create a new session
        
        session.setAttribute("username", username);
    }
    
    public static void main(String[] args) {
        HttpServletRequest request = // Obtain the request object
        
        String username = "admin";
        login(request, username);
        
        // Proceed with authenticated actions
    }
}

In this compliant code, the login method now performs proper session management. It first invalidates the existing session using the invalidate method, which ensures that any existing session data is cleared. Then, it creates a new session using request.getSession(true), which generates a new session identifier. This mitigates the session fixation vulnerability by ensuring that each user receives a fresh session identifier upon login, preventing an attacker from fixing the session identifier in advance.

Inclusion of Functionality from Untrusted Control

Noncompliant code:

import java.io.File;
import java.io.IOException;

public class UntrustedFunctionalityExample {
    public static void processFile(String filename) {
        try {
            File file = new File(filename);
            // Process the file contents
        } catch (IOException e) {
            // Handle file processing error
        }
    }
    
    public static void main(String[] args) {
        String userProvidedFilename = "userfile.txt";
        processFile(userProvidedFilename);
    }
}

In this noncompliant code, the processFile method accepts a user-provided filename as input and attempts to process the contents of the file. However, it directly uses the user-provided filename to create a File object without performing any validation or sanitization. This introduces the risk of including functionality from an untrusted source, as an attacker can manipulate the filename to potentially access sensitive files or perform arbitrary file operations.

To address this issue, here’s a compliant code example:

Compliant code:

import java.io.File;
import java.io.IOException;

public class UntrustedFunctionalityExample {
    public static void processFile(String filename) {
        // Validate and sanitize the filename before processing
        if (isValidFilename(filename)) {
            try {
                File file = new File(filename);
                // Process the file contents
            } catch (IOException e) {
                // Handle file processing error
            }
        } else {
            // Handle invalid filename
        }
    }
    
    public static boolean isValidFilename(String filename) {
        // Implement validation logic to ensure the filename is safe
        // e.g., restrict file path, disallow certain characters, etc.
        return true;
    }
    
    public static void main(String[] args) {
        String userProvidedFilename = "userfile.txt";
        processFile(userProvidedFilename);
    }
}

In this compliant code, a separate isValidFilename method is introduced to validate and sanitize the user-provided filename before processing it. The isValidFilename method should implement proper validation logic to ensure that the filename meets the desired criteria (e.g., restrict file path, disallow certain characters, etc.). Only if the filename passes the validation, it proceeds with processing the file contents. Otherwise, it handles the case of an invalid filename appropriately. By validating and sanitizing the input, the code mitigates the risk of including functionality from untrusted control and helps ensure that only safe and expected filenames are processed.

Download of Code Without Integrity Check

Noncompliant code:

import java.io.IOException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;

public class CodeDownloadExample {
    public static void downloadCode(String url, String destination) {
        try {
            URL codeUrl = new URL(url);
            Path destinationPath = Path.of(destination);
            Files.copy(codeUrl.openStream(), destinationPath, StandardCopyOption.REPLACE_EXISTING);
        } catch (IOException e) {
            // Handle download error
        }
    }
    
    public static void main(String[] args) {
        String codeUrl = "http://example.com/malicious-code.jar";
        String destinationPath = "/path/to/save/malicious-code.jar";
        downloadCode(codeUrl, destinationPath);
    }
}

In this noncompliant code, the downloadCode method accepts a URL and a destination path where the code will be downloaded. It directly opens a connection to the specified URL and downloads the code without performing any integrity check or verification. This approach leaves the code vulnerable to the download of malicious or tampered code, which can lead to security risks and potential exploitation.

To address this issue, here’s a compliant code example:

Compliant code:

import java.io.IOException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

public class CodeDownloadExample {
    public static void downloadCode(String url, String destination) {
        try {
            URL codeUrl = new URL(url);
            Path destinationPath = Path.of(destination);
            
            // Download the code to a temporary file
            Path tempPath = Files.createTempFile("downloaded_code", ".tmp");
            Files.copy(codeUrl.openStream(), tempPath, StandardCopyOption.REPLACE_EXISTING);
            
            // Calculate the checksum of the downloaded code
            String checksum = calculateChecksum(tempPath);
            
            // Verify the integrity of the downloaded code
            if (isValidChecksum(checksum)) {
                // Move the downloaded code to the destination path
                Files.move(tempPath, destinationPath, StandardCopyOption.REPLACE_EXISTING);
            } else {
                // Handle integrity check failure
                Files.deleteIfExists(tempPath);
            }
        } catch (IOException e) {
            // Handle download error
        }
    }
    
    public static String calculateChecksum(Path filePath) throws IOException {
        try {
            MessageDigest md = MessageDigest.getInstance("SHA-256");
            byte[] fileBytes = Files.readAllBytes(filePath);
            byte[] checksumBytes = md.digest(fileBytes);
            StringBuilder checksumBuilder = new StringBuilder();
            for (byte b : checksumBytes) {
                checksumBuilder.append(String.format("%02x", b));
            }
            return checksumBuilder.toString();
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("Error calculating checksum.", e);
        }
    }
    
    public static boolean isValidChecksum(String checksum) {
        // Compare the calculated checksum with a trusted value
        String trustedChecksum = "e1a7a76c51a1024193a54f95e3dbaeaeaa01a7544c24404db4c24bdf8a34937e";
        return trustedChecksum.equals(checksum);
    }
    
    public static void main(String[] args) {
        String codeUrl = "http://example.com/malicious-code.jar";
        String destinationPath = "/path/to/save/malicious-code.jar";
        downloadCode(codeUrl, destinationPath);
    }
}

Deserialization of Untrusted Data

Noncompliant code:

import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;

public class DeserializationExample {
    public static void main(String[] args) {
        String serializedData = "serialized_data.ser";
        
        try (FileInputStream fileIn = new FileInputStream(serializedData);
             ObjectInputStream in = new ObjectInputStream(fileIn)) {
            
            Object obj = in.readObject();
            // Process the deserialized object
            
        } catch (IOException | ClassNotFoundException e) {
            // Handle deserialization error
        }
    }
}

In this noncompliant code, the DeserializationExample class attempts to deserialize an object from a serialized file using ObjectInputStream. However, it does not perform any validation or checks on the deserialized data, making it vulnerable to attacks such as remote code execution, object injection, or deserialization of malicious data.

To address this issue, here’s a compliant code example:

Compliant code:

import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;

public class DeserializationExample {
    public static void main(String[] args) {
        String serializedData = "serialized_data.ser";
        
        try (FileInputStream fileIn = new FileInputStream(serializedData);
             ObjectInputStream in = new ObjectInputStream(fileIn)) {
            
            // Perform validation on the deserialized object
            Object obj = in.readObject();
            if (isValidObject(obj)) {
                // Process the deserialized object
            } else {
                // Handle invalid or malicious object
            }
            
        } catch (IOException | ClassNotFoundException e) {
            // Handle deserialization error
        }
    }
    
    public static boolean isValidObject(Object obj) {
        // Implement validation logic based on the expected object type
        // and any additional validation criteria
        
        // Example: Ensure the deserialized object is of the expected type
        return obj instanceof MySerializableClass;
    }
}

In this compliant code, the deserialization process includes a validation step before processing the deserialized object. The isValidObject method is used to perform validation based on the expected object type and any additional validation criteria. This helps prevent the deserialization of untrusted or malicious data by ensuring that the deserialized object meets the expected criteria.

Insufficient Logging

Noncompliant code:

public class PaymentService {
    private static final Logger logger = Logger.getLogger(PaymentService.class.getName());

    public void processPayment(String paymentData) {
        // Process the payment
        // ...

        // Log the payment result
        logger.info("Payment processed successfully");
    }
}

In this noncompliant code, the PaymentService class processes a payment but only logs a generic message indicating a successful payment. The logging is insufficient because it lacks essential information such as the user’s identity, the payment amount, and any relevant contextual details. This makes it challenging to investigate and trace payment-related issues or potential security incidents.

To address this issue, here’s a compliant code example:

Compliant code:

public class PaymentService {
    private static final Logger logger = Logger.getLogger(PaymentService.class.getName());

    public void processPayment(String paymentData, User user) {
        // Process the payment
        // ...

        // Log the payment result with relevant information
        logger.info("Payment processed successfully. User: " + user.getUsername() + ", Amount: " + paymentData.getAmount());
    }
}

In this compliant code, the processPayment method now accepts an additional parameter User to capture the user’s information. The relevant information, such as the user’s username and payment amount, is included in the log message. By providing more detailed and contextual information in the log, it becomes easier to track and investigate payment-related events or security incidents.

Improper Output Neutralization for Logs

Noncompliant code:

public class LoginService {
    private static final Logger logger = Logger.getLogger(LoginService.class.getName());

    public void logInvalidLogin(String username) {
        // Log the invalid login attempt
        logger.info("Invalid login attempt: " + username);
    }
}

In this noncompliant code, the logInvalidLogin method logs an invalid login attempt by directly concatenating the username into the log message. This approach can lead to log injection or log forging attacks if the username contains special characters or control characters.

To address this issue, here’s a compliant code example that applies proper output neutralization:

Compliant code:

public class LoginService {
    private static final Logger logger = Logger.getLogger(LoginService.class.getName());

    public void logInvalidLogin(String username) {
        // Sanitize the username to prevent log injection
        String sanitizedUsername = sanitize(username);

        // Log the invalid login attempt with the sanitized username
        logger.info("Invalid login attempt: " + sanitizedUsername);
    }

    private String sanitize(String input) {
        // Implement appropriate sanitization logic
        // ...
        return input.replaceAll("[^a-zA-Z0-9]", "");
    }
}

In this compliant code, the sanitize method is introduced to properly neutralize the output by removing any potentially malicious or unwanted characters from the username. The sanitize method can be customized based on the specific requirements and context of the application. By applying proper output neutralization techniques, the risk of log injection or log forging attacks is mitigated, ensuring the integrity and reliability of the log data.

Omission of Security-relevant Information

Noncompliant code:

public class PaymentService {
    public void processPayment(String creditCardNumber, double amount) {
        // Process the payment

        // Log the payment without including security-relevant information
        Logger.getLogger(PaymentService.class.getName()).info("Payment processed");
    }
}

In this noncompliant code, the processPayment method processes a payment but fails to include security-relevant information in the log message. This omission can make it difficult to track and investigate any security-related issues or anomalies related to the payment processing.

To address this issue, here’s a compliant code example that includes security-relevant information in the log message:

Compliant code:

public class PaymentService {
    public void processPayment(String creditCardNumber, double amount) {
        // Process the payment

        // Log the payment with security-relevant information
        Logger logger = Logger.getLogger(PaymentService.class.getName());
        logger.info("Payment processed - Credit Card: " + maskCreditCardNumber(creditCardNumber) + ", Amount: " + amount);
    }

    private String maskCreditCardNumber(String creditCardNumber) {
        // Mask the credit card number for security purposes
        // ...
        return "************" + creditCardNumber.substring(creditCardNumber.length() - 4);
    }
}

In this compliant code, the log message is enhanced to include the masked credit card number and the payment amount. The maskCreditCardNumber method is introduced to obfuscate the sensitive credit card number and ensure its security during logging. By including security-relevant information in the log message, administrators and security analysts can better monitor and investigate payment-related activities, facilitating incident response and security analysis.

Sensitive Information into Log File

Noncompliant code:

public class UserService {
    private static final Logger logger = Logger.getLogger(UserService.class.getName());

    public void createUser(String username, String password) {
        // Create the user

        // Log the sensitive information
        logger.info("User created - Username: " + username + ", Password: " + password);
    }
}

In this noncompliant code, the createUser method logs sensitive information, such as the username and password, directly into the log file. Storing sensitive data in log files can pose a significant security risk, as log files may be accessible to unauthorized individuals or stored indefinitely, potentially exposing sensitive information.

To address this issue, here’s a compliant code example that avoids logging sensitive information:

Compliant code:

public class UserService {
    private static final Logger logger = Logger.getLogger(UserService.class.getName());

    public void createUser(String username, String password) {
        // Create the user

        // Log a message without sensitive information
        logger.info("User created - Username: " + username);
    }
}

In this compliant code, the logging statement is modified to exclude the password. Only the username is logged, while the password is omitted from the log message. By avoiding the logging of sensitive information, the risk of exposing sensitive data in log files is mitigated, enhancing the overall security posture of the application.

Server-Side Request Forgery (SSRF)

Noncompliant code:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;

public class ImageProcessor {
    public void processImage(String imageUrl) throws IOException {
        // Retrieve image from the provided URL
        URL url = new URL(imageUrl);
        BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream()));
        // Process the image
        // ...
    }
}

In this noncompliant code, the processImage method accepts an imageUrl as input and directly makes a request to that URL to retrieve an image. This code is vulnerable to SSRF because it allows an attacker to specify any URL, including internal network resources or malicious URLs, leading to potential attacks against internal systems or services.

To address this SSRF vulnerability, here’s a compliant code example that implements proper URL validation and restricts the allowed domains:

Compliant code:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;

public class ImageProcessor {
    private static final String ALLOWED_DOMAIN = "example.com";

    public void processImage(String imageUrl) throws IOException {
        // Validate the URL
        URL url = new URL(imageUrl);
        String host = url.getHost();
        
        if (!host.endsWith(ALLOWED_DOMAIN)) {
            throw new IllegalArgumentException("Invalid image URL");
        }

        // Retrieve image from the provided URL
        BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream()));
        // Process the image
        // ...
    }
}

In this compliant code, the URL is validated by checking the host against an allowed domain (e.g., “example.com”). If the URL does not belong to the allowed domain, an exception is thrown. This ensures that only trusted URLs are processed and mitigates the risk of SSRF attacks by restricting requests to specific domains.

It’s important to note that URL validation can be more complex depending on the specific requirements of your application. This example demonstrates a basic approach, but it’s recommended to use a well-tested library or framework for URL parsing and validation to handle various edge cases and potential vulnerabilities effectively.