Working with LDAP Authentication
1. Understanding LDAP Protocol
| Aspect | Detail |
| Spec | RFC 4511 (LDAPv3) |
| Ports | 389 (ldap), 636 (ldaps), 3268/3269 (AD GC) |
| Data model | DIT (Directory Information Tree) of entries with DN |
| Schema | objectClass + attributes (inetOrgPerson, person, user) |
2. Implementing LDAP Bind
| Bind Type | Use |
| Simple | DN + password (clear → require TLS) |
| SASL | GSSAPI / DIGEST-MD5 / EXTERNAL (mTLS) |
| Anonymous | Lookup-only (often disabled) |
3. Using Simple Bind
Example: Spring LDAP authentication
// Two-step: search by username, then bind with DN+password
DirContextOperations ctx = ldapTemplate.searchForContext(
LdapQueryBuilder.query().where("uid").is(username));
String userDn = ctx.getNameInNamespace();
new LdapTemplate(new LdapContextSource() {{
setUrl("ldaps://ldap.example.com:636");
setUserDn(userDn);
setPassword(password);
}}).lookup(""); // throws on bad credentials
4. Implementing SASL Authentication
| Mechanism | Notes |
| GSSAPI | Kerberos-based, SSO |
| EXTERNAL | Uses TLS client cert identity |
| DIGEST-MD5 DEPRECATED | Weak hash |
| SCRAM-SHA-256 | Modern challenge–response |
5. Querying LDAP Directory
| Filter | Example |
| Equality | (uid=alice) |
| Substring | (cn=Al*) |
| AND | (&(objectClass=user)(department=eng)) |
| OR | (|(uid=a)(uid=b)) |
| Member check (AD) | (memberOf=CN=Admins,...) |
| Nested (AD) | memberOf:1.2.840.113556.1.4.1941: |
Warning: Always escape user input in LDAP filters — LDAP injection allows auth bypass via crafted *, (, ), \.
6. Using LDAP with Active Directory
| AD Attribute | Use |
| sAMAccountName | Pre-Windows 2000 logon name |
| userPrincipalName | email-style (user@domain.com) |
| objectGUID | Immutable identifier |
| memberOf | Group memberships (non-transitive) |
| userAccountControl | Bit flags (disabled, locked, etc.) |
| Global Catalog | Port 3268, forest-wide subset |
7. Implementing LDAP Connection Pooling
| Setting | Detail |
| Pool size | 10–50 connections per node |
| Max idle | 5 min |
| Validation | Periodic RootDSE read |
| Failover | Multiple LDAP URIs, retry on error |
| Library | UnboundID LDAP SDK, ldapjs (Node), python-ldap |
8. Validating LDAP Certificates
| Concern | Mitigation |
| MitM | Always use ldaps:// or StartTLS |
| Trust store | Pin internal CA cert |
| Hostname | Verify SAN matches LDAP URI |
| Channel binding | Required for modern AD (LDAP signing/sealing) |
9. Implementing LDAP Group Membership
Example: Resolve groups
String filter = "(&(objectClass=user)(sAMAccountName={0}))";
DirContextOperations user = template.searchForContext(
query().base("OU=Users,DC=example,DC=com")
.searchScope(SUBTREE)
.filter(filter, username));
String[] groups = user.getStringAttributes("memberOf"); // distinguished names
10. Handling LDAP Authentication Errors
| Result Code | Meaning |
| 49 (invalidCredentials) | Bad password or unknown user |
| 49 + 533 | Account disabled (AD subcode) |
| 49 + 532 | Password expired (AD) |
| 49 + 775 | Account locked (AD) |
| 50 | insufficientAccessRights |
| 51 | busy — retry with backoff |
| 53 | unwillingToPerform (e.g., password complexity) |