LDAP Client Best Practices
By mcraig on Aug 21, 2009
In 2006 Ludo helped me
compose a list of LDAP best practices for a developer guide I was
writing for Sun. This chapter of the guide was removed before
When Developing Specify LDAP v3
Many client libraries default to LDAP v2, but you can elect to use LDAP v3. To benefit from LDAP v3 features, you can set up the connection, and then authenticate explicitly using LDAP v3.
With JNDI, you could use LDAP v3 as shown here.
import java.util.Hashtable; import javax.naming.ldap.InitialLdapContext; Hashtable env = new Hashtable(); env.put("java.naming.ldap.version", "3"); InitialLdapContext ctx = new InitialLdapContext(env, null);
With the Mozilla LDAP C SDK, you could use LDAP v3 as shown here.
#include "ldap.h" int version = LDAP_VERSION3; ldap_set_option( NULL, LDAP_OPT_PROTOCOL_VERSION, &version );
Mozilla LDAP C SDK uses LDAP v3 by default.
With the Mozilla LDAP Java SDK, you could use LDAP v3 as shown here.
import netscape.ldap.LDAPConnection; LDAPConnection ld = new LDAPConnection(); ld.setOption(LDAPv3.PROTOCOL_VERSION, new Integer(3));
When Developing Authenticate Correctly
In LDAP v3, you connect, then you bind and perform LDAP operations, then you unbind and disconnect. The bind is the authentication operation in LDAP. Your application can hold onto a connection but change the authentication credentials by using the bind operation again.
Some directories do not allow anonymous access, even for reads. When you build your application, keep the option that allows users to authenticate to the directory. Furthermore, the information sent across the network can be sensitive. You can protect sensitive data by allowing the application to secure the connection by using Secure Sockets Layer (SSL) or Start Transport Layer Security (TLS).
If your application needs to authenticate, obtain a regular account to authenticate with the directory, rather than using the directory superuser account (such as cn=Directory Manager). When you authenticate as directory superuser, you bypass normal access control mechanisms. Bypassing normal access control renders auditing directory access more difficult.
When authenticating, have your application use SSL or SASL DIGEST MD5 to avoid sending passwords over the network in clear text. Furthermore, when using password-based authentication, have your application check password policy controls, especially to determine when a password must be renewed.
When Developing Limit Connection Overhead
A new connection requires system resources. The LDAP model allows you to reuse connections by binding again with a different identity on the same connection. Thus, you can avoid the costs of new connections, particularly negotiated connections such as connections that use SSL, by reusing connections. Your application can use a pool of connections, rebinding when necessary. Your application can alternatively use the proxy authorization control to remain authenticated as the application but perform operations on behalf of a particular user.
When establishing a connection, your application can provide alternate server host names and port numbers to facilitate failover that is transparent to the application. You can also set time limits for LDAP operations to avoid getting blocked.
When finished with a connection, your application should perform an unbind.
When Developing Handle Potential Inactivity Timeouts
Most network equipment can use timeouts to drop stale connections, ensuring the equipment keeps a maximum number of connections that are available.
If your application pools connections or opens connections for persistent search, than guard against timeouts that drop those connections. Use the connections occasionally to reset inactivity timers present in the network.
Alternatively, if you have control over the connection, consider disabling inactivity time outs for your applications that need to keep persistent connections open. Load balancers and proxy software often use inactivity timeouts.
When Developing Retrieve Entries Intelligently
LDAP servers typically respond quickly to requests for entries. Yet, the server can respond most quickly when your application asks it to do only necessary work. If you need to read only a few attributes in an entry, request each attribute explicitly. Avoid reading the entire entry, then parsing the entire entry to obtain the required data.
Furthermore, when you do request attributes in an entry, retrieve all the required attributes at once. Each new request involves a new operation on the server.
If any of the attributes that you require are operational attributes, you must request those attributes specifically. With Sun Directory Server, operational attributes are identifiable in the directory schema by their USAGE, which is directoryOperation or dsaOperation.
When retrieving entries and attributes, recognize that you might not have access to all the attributes that exist.
When Developing Write Simple, Conforming LDAP Filters
The best filters use attributes that are indexed according to the way the attributes are indexed. For example, if employeeNumber is indexed for equality, your filter should be an equality filter such as (employeeNumber=12345). Do not use a substring filter instead.
Avoid deeply nested complex filters when you can. When you must use complex filters, place the most specific filters first to narrow the list of candidate entries the directory must check. For best results, use not, !, only with and, &, for example (&(cn=Barbara)(!(sn=Jensen))). When you use not with or in a filter, the directory must construct a candidate list of everything except what your filter specifies.
When Developing Make Modifications Specific
Modifications are atomic on the entry to which the modifications apply. When modifying multivalued attributes, delete and replace specific values. Do not replace an entire list of multiple values to change only a few values. Replacing specific values is particularly good practice when the changes must be replicated across a set of servers.
Moreover, when you have very large values to store in an attribute, store a reference to the data instead of storing the data object.
When Developing Trust Result Codes
LDAP replication trades tight consistency across replica servers for very high performance, availability, and scalability. By allowing loose consistency of data across sets of replica servers, individual servers can respond very quickly to your application. Yet, data replication is not instantaneous. A short but detectable delay can ensue after a server returns success for a write operation, but before the effects are seen on other replicas.
Therefore, when your application receives a result code from an LDAP to indicate that an operation was successful, your application should trust the result code. When application requests are balanced across replicas, reading from another replica might result in errors due to a slight delay in replication.
Directory administrators can get around applications that do not
result codes for example by configuring Sun Directory Proxy Server to
affinity, which routes operations from the same client to the same
When Developing Limit Dealings With Groups and RolesWhen you want to know whether an account belongs to a group or a role, read only the necessary attribute values. Do not read the entire list of group members.
For static groups, Sun Directory Server and OpenDS offer the isMemberOf attribute on the user entry. With Sun Directory Server 5.x versions, compare the DN of the account to the uniqueMember attribute of the group, such as (uniquemember=uid=bjensen,ou=people,dc=example,dc=com). Or use a filter to find all the static groups to which a user belongs, such as (&(member=uid=bjensen,ou=people,dc=example,dc=com)(objectclass=groupofnames)).
For dynamic groups, do the following:
Read the URL from the group definition.
Examine the host, DN, and scope of the URL.
Apply the filter part of the URL to the entry for the account.
For Sun Directory Server roles, compare the DN of the role to the nsRole attribute of the entry for the account, such as (nsrole=cn=management,ou=people,dc=example,dc=com).
When Developing Read the DSEThe root DSE is the entry that is retrieved by ldapsearch -b "" -s base "(objectclass=\*)". The root DSE describes server capabilities. The root DSE contains information about supported LDAP protocol versions, naming contexts (suffixes), LDAPv3 controls, LDAPv3 extensions, and authentication mechanisms. The root DSE can contain information about the server version.
Some directory administrators protect access to the root DSE. Yet, applications might read the root DSE to confirm that the server in fact supports functionality required by applications.
When Developing Use Resource-Intensive Features Sparingly
Directories offer powerful features that can nevertheless place a heavy load on the server. Two such features are persistent search, and server-side sorting.
Persistent search lets you start a search that does not stop when complete, but instead allows you to receive updates when entries are modified. To provide this feature, the server must handle your search when anything happens to an entry in its scope.
Server-side sorting requires that the server sort the entries that are returned during a search. Instead of returning entries as quickly as possible, the server must therefore get the list to return, and sort the list.
When Developing Avoid Hard Coding Information About Data
The container entry for a subtree might be not be identical on different directories. Rather than hard code the container entry throughout your application, locate the container entry. Then navigate beneath the container entry in the tree.
Object classes and attribute types for the same information can also differ from directory to directory. Use configuration files, properties files, or other easily modifiable variables rather than hard coding object class and attribute type identifiers into your application.
Be aware as well that object class and attribute type identifiers are not case-sensitive in LDAP. Your application should therefore recognize that inetOrgPerson and inetorgperson are equivalent, as are isMemberOf and ismemberof.
When Developing Define Schemas Only When NecessarySchemas define the object classes and attribute types that are recognized by the directory. If your application can use a standard schema, use the standard schema. LDAP server schemas typicall define numerous standard object classes, and attribute types.
When you must define your own schema objects, follow these guidelines:
Extend existing object classes by using AUXILIARY classes.
Create new attributes rather than redefining existing attributes.
Other applications might depend on existing attributes to keep their existing semantics.
Obtain new object identifiers for the schema elements you define, rather than reusing existing object identifiers.
Obtain new names for the schema elements you define, rather than reusing existing names.
Update schema over LDAP if you can.
When Developing Handle ReferralsLDAPv3 allows directories that are unable to handle your request to refer your application to other directories. Your application should follow those referrals.
When following referrals, realize that authentication procedures might not be exactly the same on different directories. Also, directories that refer to each other could potentially cause a referral loop. With the Mozilla Directory SDKs, you can limit referral hops to prevent your application from being referred endlessly from one directory to another directory. The JNDI interface enables you to follow referrals automatically.
When Developing Treat a Directory as a Directory
A directory is typically a repository for identity data, and for information that you expect to keep for awhile and read often. You might typically find relational databases better adapted to hold transient data such as session keys and presence information, or voluminous accumulated data such as application logs.
When Troubleshooting Check Result CodesWhen an LDAP request from your application fails on the server, the server sends back a result code, and possibly an explanatory message. Your application should check the result codes, and for explanatory messages. Common failure result codes include the following, which are expressed as decimal values. Others result codes are defined as well.
LDAP operations error. The server encountered an error while processing your request.
No such object. The entry is not present on the server. Also, no referral is defined for the entry.
Invalid credentials. Your application failed to authenticate properly.
LDAP unwilling to perform. The directory does not support the request. Alternatively, the directory is not currently in a state in which to complete your request. For example, the directory might be in read-only mode when your application requests a modification.
Object class violation. Your write request would cause an entry to no longer conform to the schema defined for the directory.
Already exists. Your application is requesting to add an entry that has the same DN as an entry already present in the directory.
RFC 4511 defines LDAP error codes.
When Troubleshooting Check Server Log Files
OpenDS and Sun Directory Server log messages related to server
operation in its instance-path/logs/errors
file. If you have access
to this file, you might find useful troubleshooting information there.
When debugging your application against Sun Directory Server, you can
adjust the log level using the dsconf set-log-prop error level:your-setting
command. Log level settings are explained
in the log(5dsconf) man
page. If you use OpenDS, see https://www.opends.org/wiki/page/HowToConfigureLogsWithDsconfig.
When Troubleshooting Inspect Network Packets
Although LDAP is not a textual protocol, tools such as snoop(1M), ethereal, and tcpdump can decode the packets, sometimes providing you with important debugging information. SLAMD also provides an LDAP decoder to display LDAP packets in human-readable format.
(How many of those rules did I break in Java, PHP, and Python? ;-)