Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: pulled out abstract ldap injection example

...

* This is a character sequence

...

LDAP Injection Example

For the purpose of this example, consider Consider an LDAP Data Interchange Format (LDIF) file that contains records in the following format:

Code Block
dn: dc=example,dc=com
objectclass: dcobject
objectClass: organization
o: Some Name
dc: example

dn: ou=People,dc=example,dc=com
ou: People
objectClass: dcobject
objectClass: organizationalUnit
dc: example

dn: cn=Manager,ou=People,dc=example,dc=com
cn: Manager
sn: John Watson
# Several objectClass definitions here (omitted)
userPassword: secret1
mail: john@holmesassociates.com

dn: cn=Senior Manager,ou=People,dc=example,dc=com
cn: Senior Manager
sn: Sherlock Holmes
# Several objectClass definitions here (omitted)
userPassword: secret2
mail: sherlock@holmesassociates.com

A search for a valid username and password often takes the form:

Code Block
(&(sn=<USERSN>)(userPassword=<USERPASSWORD>))

However, an attacker could bypass authentication by using S* for the USERSN field and * for the USERPASSWORD field. Such input would yield every record whose USERSN field began with S.

An authentication routine that permitted LDAP injection would allow unauthorized users to log in. Likewise, a search routine would allow an attacker to discover part or all of the data in the directory.

Noncompliant Code Example

This noncompliant code example allows a caller of the method searchRecord() to search for a record in the directory using the LDAP protocol. The string filter is used to filter the result set for those entries that match a user name and password supplied by the caller. When a malicious user enters specially crafted input, as outlined above, this elementary authentication scheme fails to confine the output of the search query to the information for which the user has access privileges. For example, the user may see any record beginning with "S" by supplying the values S* and * for the string variables userSN and UserPassword respectively. Consequently, an attacker can discover information about any user without prior knowledge of a user and password pair.

Code Block
bgColor#FFCCCC
// String userSN = "S*"; // Invalid
// String userPassword = "*"; // Invalid
public class LDAPInjection {        
  private void searchRecord(String userSN, String userPassword) throws NamingException {        
    Hashtable<String, String>  env = new Hashtable<String, String>();
    env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
    try {
      DirContext dctx = new InitialDirContext(env);
		    
      SearchControls sc = new SearchControls();
      String[] attributeFilter = {"cn", "mail"};
      sc.setReturningAttributes(attributeFilter);
      sc.setSearchScope(SearchControls.SUBTREE_SCOPE);
      String base = "dc=example,dc=com";

      // The following resolves to (&(sn=S*)(userPassword=*))      
      String filter = "(&(sn=" + userSN + ")(userPassword=" + userPassword + "))"; 

      NamingEnumeration<?> results = dctx.search(base, filter, sc);
      while (results.hasMore()) {
        SearchResult sr = (SearchResult) results.next();
        Attributes attrs = sr.getAttributes();
        Attribute attr = attrs.get("cn");
        System.out.println(attr.get());
        attr = attrs.get("mail");
        System.out.println(attr.get());
      }
      dctx.close();
    } catch (NamingException e) {
      // Handle
    }
  }
}

...

This compliant solution uses a white-list to sanitize user input so that the filter string contains only valid characters. For exampleIn this code, userSN may contain only letters and spaces whereas a password may contain only alphanumeric characters.

Code Block
bgColor#ccccff
// String userSN = "Sherlock Holmes"; // Valid
// String userPassword = "secret2";   // Valid

sc.setSearchScope(SearchControls.SUBTREE_SCOPE);
String base = "dc=example,dc=com";
           
if(!userSN.matches("[\\w\\s]*") || !userPassword.matches("[\\w]*")) {
  throw new IllegalArgumentException("Invalid input");
} 
            	
String filter = "(&(sn = " + userSN + ")(userPassword=" + userPassword + "))";      

...