Description
An anonymous LDAP bind does not establish the client’s identity. The server’s anonymous-access permissions determine which directory data can actually be read or changed. Allowing anonymous access to sensitive data or modification operations may expose information or permit unauthorized changes.
Potential impact
- Sensitive data exposure: Anonymous clients may read user information or group lists when the server permits it.
- Unauthorized changes: Anonymous write permissions may allow data manipulation and, depending on the affected data, privilege escalation.
- Service abuse: Large volumes of anonymous requests may contribute to denial of service.
Remediation
- Explicitly configure a server-supported authentication mechanism for protected connections. Use verified TLS with
"simple"authentication; do not assume that the string"strong"alone configures secure SASL. - Use a least-privileged account and obtain its password from a secret store or a restricted execution environment. Reject connections when the password is missing or empty.
- Deny anonymous reads and writes to sensitive data on the server. Changing client authentication settings does not change the server’s anonymous permissions.
Examples
Before
java
import javax.naming.Context;
import javax.naming.directory.InitialDirContext;
import java.util.Hashtable;
public class UnsafeLDAP {
public static void main(String[] args) throws Exception {
Hashtable<String, String> env = new Hashtable<>();
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL, "ldap://ldap.example.org:389");
env.put(Context.SECURITY_AUTHENTICATION, "none"); // Request an anonymous bind.
InitialDirContext ldapContext = new InitialDirContext(env);
}
}
After
This requires an LDAPS server and a trust store that allow certificate and hostname verification. app-reader is an example account with only the required read permissions; replace it with the actual account DN.
java
import javax.naming.Context;
import javax.naming.directory.InitialDirContext;
import java.util.Hashtable;
public class SecureLDAP {
public static void main(String[] args) throws Exception {
Hashtable<String, String> env = new Hashtable<>();
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL, "ldaps://ldap.example.org:636");
env.put(Context.SECURITY_AUTHENTICATION, "simple"); // Authenticate with a password over TLS.
env.put(Context.SECURITY_PRINCIPAL, "cn=app-reader,dc=example,dc=org");
env.put(Context.SECURITY_CREDENTIALS, getLdapPassword()); // Load the password from the runtime environment.
InitialDirContext ldapContext = new InitialDirContext(env);
ldapContext.close();
}
private static String getLdapPassword() {
// Validate the password supplied by the restricted execution environment.
String password = System.getenv("LDAP_PASSWORD");
if (password == null || password.isEmpty()) {
throw new IllegalStateException("LDAP password is not set");
}
return password;
}
}
Explanation:
- Before: Requests an anonymous bind with
"none". Access remains limited by the server’s permissions for anonymous clients. - After: Authenticates over TLS using a nonempty, externally supplied password. Environment variables are not themselves a secret store, so protect execution-environment access and logs as well.