Account Takeover - Java

Account Takeover - Java

Need

Prevention of unauthorized access and control of user accounts

Context

  • Usage of Java for building cross-platform applications
  • Usage of javax.servlet for Java web application development
  • 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 {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String username = request.getParameter("username");
        String password = request.getParameter("password");

        User user = Database.getUser(username);

        if (user != null && user.getPassword().equals(password)) {
            HttpSession session = request.getSession();
            session.setAttribute("user", user);
            response.sendRedirect("/home");
        } else {
            response.sendRedirect("/login");
        }
    }
}
        
        

The above code is a simple example of a login servlet using the javax.servlet package in Java. This servlet handles POST requests containing a username and password, and it attempts to authenticate the user by checking if the provided username and password match an existing user's credentials in the database.

Here are the steps of how it works:

1. The servlet receives a POST request containing a username and password. 2. It retrieves the user object associated with the username from the database. 3. If the user object is not null and the password matches the user's password, it creates a new session, stores the user object in the session, and redirects the user to the home page. 4. If the user object is null or the password does not match, it redirects the user back to the login page.

This code is vulnerable to account takeover attacks because it relies solely on username and password for authentication. If an attacker can obtain a user's credentials through methods like phishing, keystroke logging, or brute force attacks, they can easily impersonate the user and take over their account.

Additionally, the code does not implement any form of rate limiting or account lockout mechanisms, making it susceptible to brute force attacks where an attacker can make unlimited login attempts until they guess the correct password.

Finally, the use of HTTP sessions for managing user state can also be exploited if an attacker is able to hijack a user's session, for example through session sidejacking or cross-site scripting (XSS) attacks. Once they have access to a user's session, they can impersonate the user and perform actions on their behalf.

Steps

  • Implement strong authentication mechanisms: Use strong and secure password hashing algorithms, such as bcrypt or Argon2, to store user passwords in the database.
  • Implement secure session management: Use a secure session management mechanism, such as using a unique session identifier, setting appropriate session timeouts, and enabling secure session cookie attributes.
  • Implement brute-force protection: Implement mechanisms to prevent brute-force attacks, such as account lockouts after multiple failed login attempts or introducing CAPTCHA challenges.
  • Implement multi-factor authentication (MFA): Implement MFA to add an extra layer of security, requiring users to provide additional authentication factors, such as a one-time password (OTP) or biometric authentication.
  • Implement secure password reset functionality: Implement a secure password reset mechanism that requires additional verification steps, such as sending a password reset link to the user's registered email address.
  • Regularly update and patch the application: Keep the application and its dependencies up to date by applying security patches and updates to fix any known vulnerabilities.
  • Perform security testing and code reviews: Regularly conduct security testing, such as penetration testing and code reviews, to identify and fix any potential vulnerabilities in the application code.
  • Educate users about secure practices: Provide guidance to users on creating strong passwords, avoiding password reuse, and being cautious of phishing attempts.

Compliant code

        import javax.servlet.*;
import javax.servlet.http.*;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Base64;

public class LoginServlet extends HttpServlet {
    private static final int MAX_ATTEMPTS = 5;
    private static final Map attempts = new HashMap<>();

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

        // Check for brute force attacks
        if (attempts.getOrDefault(username, 0) >= MAX_ATTEMPTS) {
            response.sendRedirect("/login?error=Too many failed attempts");
            return;
        }

        User user = Database.getUser(username);

        if (user != null) {
            // Compare hashed password
            if (checkPassword(password, user.getPassword(), user.getSalt())) {
                HttpSession session = request.getSession();
                session.setAttribute("user", user);
                response.sendRedirect("/home");
                attempts.remove(username); // reset attempts
            } else {
                attempts.put(username, attempts.getOrDefault(username, 0) + 1);
                response.sendRedirect("/login?error=Invalid username or password");
            }
        } else {
            response.sendRedirect("/login?error=Invalid username or password");
        }
    }

    private boolean checkPassword(String password, String hashedPassword, String salt) {
        return hashedPassword.equals(hashPassword(password, salt));
    }

    private String hashPassword(String password, String salt) {
        try {
            MessageDigest md = MessageDigest.getInstance("SHA-256");
            md.update(salt.getBytes());
            byte[] bytes = md.digest(password.getBytes());
            return Base64.getEncoder().encodeToString(bytes);
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        }
    }
}
        
        

The updated code includes several security enhancements to mitigate the risk of account takeover:

1. Brute-force protection: The code now keeps track of failed login attempts for each user. If a user exceeds a maximum number of failed attempts (5 in this case), they are temporarily locked out of their account.

2. Password hashing: Instead of storing and comparing passwords in plaintext, the code now hashes passwords using a secure hashing algorithm (SHA-256 in this case). This means that even if an attacker gains access to the database, they won't be able to reverse-engineer users' passwords from the hashes.

3. Salted hashes: To further enhance the security of hashed passwords, the code now uses a salt (a random string) that is added to the password before hashing. This makes it much more difficult for an attacker to use precomputed tables of hashes (rainbow tables) to crack the passwords.

Please note that this code is a basic example and might not cover all security aspects. For instance, it doesn't include multi-factor authentication (MFA), secure session management, secure password reset functionality, or user education. These should be implemented as part of a comprehensive security strategy.

References