Improved Security Functionality
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
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.
Authentication and Authorization
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:
-
ANONYMOUS -- This is basically the equivalent of establishing an
unauthenticated session, with the slight added benefit of providing a trace
string that may appear in the server log. The UnboundID LDAP SDK for Java
provides support for this mechanism, but neither JNDI nor the Netscape Directory
SDK for Java support it.
-
CRAM-MD5 -- This is a mechanism that can allow password-based
authentication over an otherwise insecure channel in a manner that will not
expose the password (although it does require that the server have access to the
clear-text password). It is similar to DIGEST-MD5 but is slightly weaker and has
fewer features. The UnboundID LDAP SDK for Java provides support for this
mechanism. The Netscape Directory SDK for Java does not. JNDI can use it
through the Java SASL framework, although this is significantly more complex
than the API exposed by the UnboundID LDAP SDK for Java.
-
DIGEST-MD5 -- This is a mechanism that can allow password-based
authentication over an otherwise insecure channel in a manner that will not
expose the password (although it does require that the server have access to the
clear-text password). It is similar to CRAM-MD5 but uses a more secure algorithm
and offers additional functionality. The UnboundID LDAP SDK for Java provides
support for this mechanism. The Netscape Directory SDK for Java does not. JNDI
can use it through the Java SASL framework, although this is significantly more
complex than the API exposed by the UnboundID LDAP SDK for Java.
-
EXTERNAL -- This is a mechanism that can be used to perform authentication
based on information that the server may have about the client that is not
directly provided as part of the LDAP protocol layer. It is most commonly in the
form of an SSL client certificate. The UnboundID LDAP SDK for Java, JNDI, and
the Netscape Directory SDK for Java all provide support for this mechanism.
-
GSSAPI -- This is a mechanism that can be used to leverage an existing
Kerberos V authentication session. The UnboundID LDAP SDK for Java provides
support for this mechanism using a relatively simple API. JNDI can use it
through the Java SASL framework, although this can be complex to use properly.
The Netscape Directory SDK for Java does not support the use of GSSAPI.
-
PLAIN -- This is a mechanism that can be used to authenticate using an
authentication ID and password, and optionally allows the specification of an
alternate authorization identity. The UnboundID LDAP SDK for Java provides
support for this mechanism. The Netscape Directory SDK for Java does not. JNDI
can use it through the Java SASL framework, although this is significantly more
complex than the API exposed by the UnboundID LDAP SDK for Java.
Password Policy Interaction
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 an UnboundID 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 an
UnboundID server. These additional capabilities include:
-
Support for the password policy control as defined in
draft-behera-ldap-password-policy. This control may be used to get warning
and/or error information about interactions with a user's password policy.
-
Support for the account usability control. This control may be used to determine
whether an account is currently usable, and if not the reason that it is
unavailable.
-
Support for the password policy state extended operation. This control may be
used to retrieve and alter virtually any kind of password policy state
information for a user.
Atomic Operations
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:
-
LDAP increment modify extension (RFC 4525) -- This enhancement to the modify
operation allows a client to request that the integer value of an attribute be
incremented or decremented by a specified amount. The client does not need to
know what the current value is, but only how much it should be incremented or
decremented. Neither JNDI nor the Netscape Directory SDK for Java directly
support this feature.
-
LDAP read entry controls (RFC 4527) -- These controls allow a client to retrieve
an entry exactly as it appeared either immediately before or immediately after
performing a write operation so that it can be sure of the contents of the
updated entry at the time of the write operation (it is particularly useful in
conjunction with the increment modify extension). Neither JNDI nor the Netscape
Directory SDK for Java include support for these controls.
-
LDAP assertion control (RFC 4528) -- This control can be used to ensure that the
target entry matches a specified filter before performing an operation against
it. This allows clients to ensure that the information that it used to determine
what to include in the update has not changed since the client read the entry.
Neither JNDI nor the Netscape Directory SDK for Java include support for this
control.
-
Batched Transactions (based on draft-zeilenga-ldap-txn) -- This specification
defines a pair of extended operations and a control that may be used to allow
multiple write operations to be processed using the same transaction and
therefore applied atomically. Neither JNDI nor the Netscape Directory SDK for
Java include support for this capability. Note, however, that because this
specification has not yet been fully standardized, it is extremely unlike that
the implementation included in the UnboundID LDAP SDK for Java will work with any
server other than the UnboundID Directory Server. As such, it is only available
in the Commercial Edition of the LDAP SDK for Java.
-
Interactive Transactions (UnboundID-specific) -- This allows a client to request
that multiple operations, including both write operations (like add, delete,
modify, and modify DN) and read operations (like search and compare), be
performed as a single atomic unit using a transaction. Neither JNDI nor the
Netscape Directory SDK for Java provide support for this capability. This
capability is only available in the Commercial Edition of the LDAP SDK for Java
and may only be used with the UnboundID Directory Server.
Search Filter Construction
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.createANDFilter(
Filter.createEqualityFilter("cn", searchText),
Filter.createEqualityFilter("sn", searchText),
Filter.createEqualityFilter("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.
Get Effective Rights Control
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 UnboundID Directory Server.
|