Directory Servers are commonly used to store sensitive information and are used to perform sensitive operations like authentication and authorization. As a result, it is very important to ensure that clients can communicate security with the server and can ensure that the requests generated are exactly what they are intended to be.
Secure communication in LDAP is generally accomplished through SSL or StartTLS. JNDI, the Netscape Directory SDK for Java, and the UnboundID LDAP SDK for Java all support communication using SSL, but the Netscape SDK does not provide any support for StartTLS.
The UnboundID LDAP SDK for Java also provides a mechanism to help simplify communication over SSL/TLS. Unfortunately, Java has traditionally made SSL/TLS communication more complex than it needs to be, but the included com.unboundid.util.ssl package provides a number of classes to make this easier. The SSLUtil class can be used to easily create SSLSocketFactory and SSLContext objects, and a number of key manager and trust manager implementations make it possible to control how client certificates are selected and whether server certificates are trusted.
Although it's not used as often as SSL/TLS, LDAP communication may also be secured through the use of SASL integrity and confidentiality. Both JNDI and the UnboundID LDAP SDK for Java support the use of SASL integrity and confidentiality when using the DIGEST-MD5 or GSSAPI mechanisms. The Netscape Directory SDK for Java does not support this functionality.
The LDAP protocol specification includes an unfortunate quirk which has been responsible for a number of significant client-side security problems in the past. It states that servers should treat simple bind operations that contain a bind DN but a zero-length password should be treated as anonymous binds. If the server receives such a request, then it will return a SUCCESS result, which may lead the client to believe that the authentication was performed as the user specified as the bind DN. This can lead to cases in which the client inappropriately allows a user to access sensitive information. To help address this problem, the UnboundID LDAP SDK for Java will not allow these kinds of bind operations by default. There is virtually no reason to allow them in the first place, and they are almost always attempts by malicious users to subvert the security of the directory-enabled application. This behavior can be disabled if the client has a legitimate reason to perform binds with a DN but no password, but such instances will likely be extremely rare.
When the client does authenticate to the server, it may be desirable to ensure that authorization identity used for the client connection is what the client expects it to be. LDAP offers two standard extensions that can be used to make this determination. The first is the authorization identity request control (as defined in RFC 3829), which indicates that the server should include the authorization identity in the bind response. The second is the "Who Am I?" extended operation (as defined in RFC 4532), which can be used to obtain the authorization identity at any time after the bind is complete. The UnboundID LDAP SDK for Java supports both of these options. Neither JNDI nor the Netscape Directory SDK for Java support either one.
It is also important to ensure that operations are processed with the appropriate authorization identity in the server. It is often infeasible and undesirable to maintain a separate connection for each user currently accessing the application, or to store the credentials and rebind to the server before processing each operation. For that reason, is is often preferable to use the proxied authorization control to request that the operation be processed under the authority the appropriate end user. The UnboundID LDAP SDK for Java supports both versions of the proxied authorization control. The Netscape Directory SDK supports only the proxied authorization V1 control but not V2. JNDI does not support either version of this control.
While the vast majority of clients perform simple authentication (which uses a bind DN and password), LDAP also allows the use of the Simple Authentication and Security Layer (SASL, as defined in RFC 4422). This can allow for authentication using alternate forms of credentials and enhanced functionality. The most commonly-used SASL mechanisms for LDAP clients include:
Some directory servers have rich password policy functionality, which can provide capabilities like ensuring users have strong passwords, requiring users to change their passwords regularly, requiring users to authenticate securely, and locking accounts after too many failed attempts. In order to provide the best possible user experience, the client should be able to interact with the directory server's password policy when appropriate.
One of the simplest ways that a client can interact with the directory server password policy is to be able to distinguish when the user's password has expired or is about to expire. This information is commonly returned in the password expired / password expiring controls as defined in draft-vchu-ldap-pwd-policy. Both the UnboundID LDAP SDK for Java and the Netscape Directory SDK for Java provide support for this control. JNDI does not.
Another key aspect of password policy integration is the ability to change user passwords in the directory. It is true that most servers support changing client passwords using standard LDAP modify operations, but RFC 3062 defines a special extended operation that may be used for this purpose as well. It also includes provisions for allowing users to include their current password as part of specifying a new password, as well as allowing the server to automatically generate a new password for the client. The UnboundID LDAP SDK for Java provides support for this password modify extended operation, while it is not available for use with either JNDI or the Netscape Directory SDK for Java.
The UnboundID LDAP SDK for Java also provides enhanced password policy interaction when it is used to communicate with a Ping Identity, UnboundID, or Nokia/Alcatel-Lucent 8661 Directory Server instance. Note, however, that these capabilities are not fully standardized and therefore they may not work with other servers and are only supported when interacting with Ping Identity, UnboundID, and Nokia/Alcatel-Lucent 8661 servers. These additional capabilities include:
Directory servers are often highly concurrent applications and can process many requests at the same time. Similarly, directory servers are generally deployed in replicated configurations so that operations can be processed at the same time across multiple servers. The nature of these kinds of environments means that it can be difficult to ensure that multiple clients aren't trying to access and/or alter the same data at the same time. If client A reads an entry from a directory and then sends an update to modify that entry based on what it read, it could be highly undesirable for client B to modify the entry after client A has performed the read but before it has performed the modify. Allowing data to be modified in unexpected ways can be a significant security problem, and it can be important to ensure that there are ways of preventing such problems.
Protection against these kinds of problems often comes in the form of various types of atomic operations. The UnboundID LDAP SDK for Java supports a number of features that may fit into this category:
One of the most common security problems for applications that interact with relational databases is that of SQL injection attacks. In this case, a malicious user knows that what gets entered into a form will be directly included in an SQL query, and that entering a specially-crafted value may result in a significantly different behavior than the application developer intended, potentially even exposing sensitive data or allowing information to be altered in unexpected ways.
While it is unlikely that this kind of attack can be used to alter the contents of a directory server, it is certainly not inconceivable that it could be used to expose sensitive data or at least more data than the application developer intended. For example, consider a simple address book lookup application with a form that allows the user to enter the name of the user for whom to search. This is a very common application, and it is very common for it to generate a filter using an algorithm that looks something like the following:
String filter = "(|(cn=" + searchText + ")(sn=" + searchText + ")(givenName=" + searchText + "))";
However, consider the case in which the user entered something like "X)(objectClass=*". The application may then get tricked into sending a search to the directory with a filter that looks like "(|(cn=X)(objectClass=*)(givenName=X)(objectClass=*)(sn=X)(objectClass=*))", and the directory may reveal much more information than it should.
One way to thwart these kinds of attacks is for developers to make sure that they either restrict the kinds of information they will allow to be entered, or to sanitize the input before using it in the filter. This can work, but it introduces the possibility that the restrictions on input data can interfere with the ability to perform legitimate searches, or that some special cases may be overlooked when the data is sanitized so that vulnerabilities remain.
The UnboundID LDAP SDK for Java provides an alternative mechanism to address this problem in the form of filter construction. Rather than creating a string representation of the filter, it is possible to construct a filter by specifying the components that it should contain. For example, the following code would produce a search filter like the one above but without the possibility of malicious input producing unexpected results:
Filter filter = Filter.and( Filter.equals("cn", searchText), Filter.equals("sn", searchText), Filter.equals("givenName", searchText));
With the above code, even if a malicious user entered something like "X)(objectClass=*" into the search field, the LDAP SDK would properly handle it and the filter that got sent to the server would actually be the equivalent of "(|(cn=X\29\28objectClass=\2A)(sn=X\29\28objectClass=\2A)(givenName=X\29\28objectClass=\2A))".
Not only does this reduce or eliminate the need to sanitize the data, it is also more efficient because the SDK doesn't have to parse the filter string because it's already given the data in the form that it needs to encode the filter for sending to the server.
The UnboundID LDAP SDK for Java provides support for a get effective rights control which may be used by clients in order to determine the capabilities that a user has when interacting with a specified entry in the directory. It can be used to determine whether a given user would have permission to perform a requested operation. This could be used to allow an application to customize the interface that it presents to a user so that operations which the user does not have rights to perform are not made available.
Note that the get effective rights control is only supported for use in conjunction with the Ping Identity, UnboundID, and Nokia/Alcatel-Lucent 8661 Directory Server products.