LDAP injection - Java

LDAP injection - Java

Need

Prevention of unauthorized data manipulation through LDAP queries

Context

  • Usage of Java for building robust and scalable applications
  • Usage of javax.naming.directory.DirContext for accessing and manipulating directory services
  • Usage of javax.naming.directory.InitialDirContext for accessing and manipulating directory services
  • Usage of javax.naming.directory.SearchControls for specifying search criteria in a directory search operation
  • Usage of javax.servlet.http.HttpServletRequest for handling HTTP requests in Java Servlets
  • Usage of HttpServletResponse for handling HTTP responses in Java Servlets
  • Usage of java.util.Hashtable for storing key-value pairs in Java

Description

Non compliant code

        import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
import javax.naming.directory.SearchControls;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Hashtable;

public class LDAPInjectionVulnerable {
    public void doGet(HttpServletRequest request, HttpServletResponse response) {
        String user = request.getParameter("user");
        String pass = request.getParameter("pass");

        Hashtable env = new Hashtable();
        env.put("java.naming.factory.initial", "com.sun.jndi.ldap.LdapCtxFactory");
        env.put("java.naming.provider.url", "ldap://localhost:389/dc=example,dc=com");

        DirContext ctx = null;
        try {
            ctx = new InitialDirContext(env);
            SearchControls controls = new SearchControls();
            controls.setSearchScope(SearchControls.SUBTREE_SCOPE);

            String filter = "(uid=" + user + ")";
            ctx.search("", filter, controls);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (ctx != null) {
                try {
                    ctx.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
        
        

In the above code, we have a servlet that accepts user and pass parameters from the HTTP request and uses these values directly to construct an LDAP filter (uid=" + user + ").

This is a classic example of an LDAP injection vulnerability because the user input is not properly sanitized before being used to construct the LDAP query. An attacker could manipulate the user parameter value to alter the LDAP query's logic.

For example, if an attacker sends a request with user parameter as *)(uid=*))(|(uid=*, the resulting filter becomes (uid=*)(uid=*))(|(uid=*), which will return all users, bypassing the intended filter logic.

This vulnerability allows an attacker to perform unauthorized actions, such as bypassing authentication, viewing sensitive information, or modifying data.

Steps

  • Validate and sanitize user input: Before using the 'user' parameter in the LDAP query, validate and sanitize the input to ensure it only contains the expected characters and does not include any malicious LDAP injection payloads.
  • Use parameterized queries: Instead of concatenating the 'user' parameter directly into the LDAP query string, use parameterized queries or prepared statements to safely include the user input in the query. This helps to prevent any unintended modification of the query.
  • Limit privileges: Ensure that the LDAP user used to connect to the server has limited privileges and only has access to the necessary resources. This helps to minimize the potential impact of any successful LDAP injection attacks.
  • Implement input validation and output encoding: Implement input validation to reject any input that does not conform to the expected format. Additionally, encode any output that is generated based on user input to prevent any potential cross-site scripting (XSS) attacks.
  • Regularly update and patch LDAP server: Keep the LDAP server software up to date with the latest security patches to mitigate any known vulnerabilities that could be exploited through LDAP injection attacks.

Compliant code

        import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
import javax.naming.directory.SearchControls;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Hashtable;
import javax.naming.directory.SearchResult;
import javax.naming.NamingEnumeration;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;

public class LDAPInjectionFixed {
    public void doGet(HttpServletRequest request, HttpServletResponse response) {
        String user = request.getParameter("user");
        String pass = request.getParameter("pass");

        // Validate and sanitize user input
        if (!user.matches("^[a-zA-Z0-9]*$")) {
            throw new IllegalArgumentException("Invalid user input");
        }

        Hashtable env = new Hashtable();
        env.put("java.naming.factory.initial", "com.sun.jndi.ldap.LdapCtxFactory");
        env.put("java.naming.provider.url", "ldap://localhost:389/dc=example,dc=com");

        DirContext ctx = null;
        try {
            ctx = new InitialDirContext(env);
            SearchControls controls = new SearchControls();
            controls.setSearchScope(SearchControls.SUBTREE_SCOPE);

            // Use parameterized queries
            String filter = "(uid={0})";
            Object[] filterArguments = new Object[]{user};
            NamingEnumeration results = ctx.search("", filter, filterArguments, controls);

            while (results.hasMore()) {
                SearchResult searchResult = results.next();
                Attributes attributes = searchResult.getAttributes();
                Attribute attribute = attributes.get("uid");
                if (attribute != null) {
                    String uid = (String) attribute.get();
                    // Perform further processing
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (ctx != null) {
                try {
                    ctx.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
        
        

The fixed code includes several measures to prevent LDAP injection attacks:

1. Input Validation and Sanitization: The user input is validated and sanitized using a regular expression to ensure it only contains alphanumeric characters. This helps to prevent any malicious LDAP injection payloads from being included in the query.

2. Parameterized Queries: Instead of concatenating the user input directly into the LDAP query string, the code uses parameterized queries. The user input is included as a separate argument to the ctx.search() method, which prevents any unintended modification of the query.

3. Limited Privileges: Although not shown in the code, it is recommended to ensure that the LDAP user used to connect to the server has limited privileges and only has access to the necessary resources. This helps to minimize the potential impact of any successful LDAP injection attacks.

4. Input Validation and Output Encoding: The code includes input validation to reject any input that does not conform to the expected format. Although not shown in the code, it is also recommended to encode any output that is generated based on user input to prevent any potential cross-site scripting (XSS) attacks.

5. Regular Updates and Patches: Although not shown in the code, it is recommended to keep the LDAP server software up to date with the latest security patches to mitigate any known vulnerabilities that could be exploited through LDAP injection attacks.

References