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
publication.
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
trust
result codes for example by configuring Sun Directory Proxy Server to
apply server
affinity, which routes operations from the same client to the same
server.
When Developing Limit Dealings With Groups and Roles
When 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 DSE
The 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 Necessary
Schemas 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 Referrals
LDAPv3 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 Codes
When 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.
|
1
|
LDAP operations error. The server encountered an error while
processing your request.
|
|
32
|
No such object. The entry is not present on the server. Also,
no referral is defined for the entry.
|
|
49
|
Invalid credentials. Your application failed to authenticate
properly.
|
|
53
|
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.
|
|
65
|
Object class violation. Your write request would cause an
entry to no longer conform to the schema defined for the directory.
|
|
68
|
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? ;-)