Skip to content

LDAP Injection Prevention Cheat Sheet / LDAP注入防护技巧清单

Introduction / 介绍

The Lightweight Directory Access Protocol (LDAP) allows an application to remotely perform operations such as searching and modifying records in directories. LDAP injection results from inadequate input sanitization and validation and allows malicious users to glean restricted information using the directory service. For general information about LDAP please visit lightweight directory access protocol (LDAP).

轻量级目录访问协议(LDAP)允许应用程序远程执行搜索和修改目录中的记录等操作。LDAP注入是由于输入清理和验证不足导致的,允许恶意用户通过目录服务窃取受限信息。有关LDAP的更多信息,请访问轻量级目录访问协议(LDAP)。

LDAP Injection is an attack used to exploit web based applications that construct LDAP statements based on user input. When an application fails to properly sanitize user input, it's possible to modify LDAP statements through techniques similar to SQL Injection.

LDAP注入是一种针对基于Web的应用程序的攻击,这些应用程序根据用户输入构建LDAP语句。当应用程序未能正确清理用户输入时,可以通过类似于SQL注入的技术修改LDAP语句。

This cheatsheet is focused on providing clear, simple, actionable guidance for preventing LDAP Injection flaws in your applications. LDAP injection attacks are common due to two factors:

本技巧清单旨在为防止应用程序中的LDAP注入缺陷提供清晰、简单、可操作的建议。LDAP注入攻击之所以常见,有两个因素:

  1. The lack of safer, parameterized LDAP query interfaces
    中文:缺乏更安全的参数化LDAP查询接口
  2. The widespread use of LDAP to authenticate users to systems.
    中文:LDAP在用户系统认证中的广泛应用

LDAP injection attacks could result in the granting of permissions to unauthorized queries, and content modification inside the LDAP tree.

LDAP注入攻击可能导致对未经授权的查询和LDAP树内的内容修改授予权限。

Primary Defenses:

主要防御措施:

  • Escape all variables using the right LDAP encoding function
    中文:使用正确的LDAP编码函数对所有变量进行转义
  • Use a framework that escapes automatically.
    中文:使用自动转义的框架。

Additional Defenses:

附加防御措施:

  • Least Privilege
    中文:最小权限
  • Allow-List Input Validation
    中文:允许列表输入验证

Primary Defenses / 主要防御

Defense Option 1: Escape all variables using the right LDAP encoding function / 防御选项1:使用正确的LDAP编码函数对所有变量进行转义

Distinguished Name Escaping / 区分名称转义

The main way LDAP stores names is based on DN (distinguished name). You can think of this like a unique identifier. These are sometimes used to access resources, like a username.

LDAP存储名称的主要方式是基于DN(区分名称)。你可以将其视为一个唯一标识符。这些有时用于访问资源,如用户名。

A DN might look like this

一个DN可能看起来像这样

cn=Richard Feynman, ou=Physics Department, dc=Caltech, dc=edu

cn=理查德·费曼, ou=物理系, dc=加州理工学院, dc=edu

or

或者

uid=inewton, ou=Mathematics Department, dc=Cambridge, dc=com

uid=艾萨克·牛顿, ou=数学系, dc=剑桥, dc=com

An allowlist can be used to restrict input to a list of valid characters. Characters and character sequences that must be excluded from allowlists — including Java Naming and Directory Interface (JNDI) metacharacters and LDAP special characters — are listed in the following list.

允许列表可以用来限制输入到有效字符列表。以下列表中列出了必须从允许列表中排除的字符和字符序列,包括Java命名和目录接口(JNDI)元字符和LDAP特殊字符:\ # + < > , ; " = 以及前后空格。

The exhaustive list is the following: \ # + < > , ; " = and leading or trailing spaces.

完整的列表(https://ldapwiki.com/wiki/Wiki.jsp?page=DN%20Escape%20Values)如下:\ # + < > , ; " = 和前后空格。

Some "special" characters that are allowed in Distinguished Names and do not need to be escaped include:

在区分名称(Distinguished Names)中允许且不需要转义的“特殊”字符包括:

* ( ) . & - _ [ ] ` ~ | @ $ % ^ ? : { } ! '

Search Filter Escaping / 搜索过滤器转义

Each DN points to exactly 1 entry, which can be thought of sort of like a row in a RDBMS. For each entry, there will be 1 or more attributes which are analogous to RDBMS columns. If you are interested in searching through LDAP for users with certain attributes, you may do so with search filters.

每个DN指向恰好一个条目,这可以类比为RDBMS中的一行。对于每个条目,将有一个或多个属性,这些属性类似于RDBMS的列。如果您想通过LDAP搜索具有特定属性的用户,可以使用搜索过滤器来完成。

In a search filter, you can use standard boolean logic to get a list of users matching an arbitrary constraint. Search filters are written in Polish notation AKA prefix notation.

在搜索过滤器中,您可以使用标准的布尔逻辑来获取匹配任意约束的用户列表。搜索过滤器使用波兰表示法,也称为前缀表示法。

Example:

示例:

(&(ou=Physics)(|
(manager=cn=Freeman Dyson,ou=Physics,dc=Caltech,dc=edu)
(manager=cn=Albert Einstein,ou=Physics,dc=Princeton,dc=edu)
))

When building LDAP queries in application code, you MUST escape any untrusted data that is added to any LDAP query. There are two forms of LDAP escaping. Encoding for LDAP Search and Encoding for LDAP DN (distinguished name). The proper escaping depends on whether you are sanitizing input for a search filter, or you are using a DN as a username-like credential for accessing some resource.

在应用程序代码中构建LDAP查询时,您必须对添加到任何LDAP查询中的任何不受信任数据进行转义。LDAP转义有两种形式:LDAP搜索编码和LDAP DN(区分名称)编码。正确的转义取决于您是在清理搜索过滤器的输入,还是将DN用作类似用户名的凭据来访问某些资源。

Some "special" characters that are allowed in search filters and must be escaped include:

在搜索过滤器中允许且必须转义的“特殊”字符包括:

* ( ) \ NUL

For more information on search filter escaping visit RFC4515.

关于搜索过滤器转义的相关信息,请访问RFC4515。

Safe Java Escaping Example / 安全的Java转义示例

The following example validates userSN with an allowlist. With Java's default predefined character classes, it accepts ASCII letters, digits, underscores, and whitespace (including tabs and line breaks); * also permits an empty string. This illustrative rule is not a general validation policy for personal names. Choose an allowlist that matches the application's requirements and apply the appropriate LDAP escaping for values outside that restricted set.

以下示例使用允许列表验证userSN。使用Java的默认预定义字符类,它接受ASCII字母、数字、下划线和空白字符(包括制表符和换行符);*也允许空字符串。此示例规则并非个人姓名的一般验证策略。请选择与应用程序要求匹配的允许列表,并应用适当的LDAP转义,以处理该受限集合之外的价值。

// String userSN = "Sherlock Holmes"; // Valid
// ... beginning of LDAPInjection.searchRecord()...
sc.setSearchScope(SearchControls.SUBTREE_SCOPE);
String base = "dc=example,dc=com";

if (!userSN.matches("[\\w\\s]*")) {
 throw new IllegalArgumentException("Invalid input");
}

String filter = "(sn = " + userSN + ")";
// ... remainder of LDAPInjection.searchRecord()... 

When a database field must include special characters, it is critical to ensure that the authentic data is stored in sanitized form in the database and also that any user input is normalized before the validation or comparison takes place. Using characters that have special meanings in JNDI and LDAP in the absence of a comprehensive normalization and allowlisting-based routine is discouraged. Special characters must be transformed to sanitized, safe values before they are added to the allowlist expression against which input will be validated. Likewise, normalization of user input should occur before the validation step (source: Prevent LDAP injection).

当数据库字段必须包含特殊字符时,确保在数据库中以清理形式存储认证数据,并在验证或比较之前对任何用户输入进行规范化,这是至关重要的。在没有全面的规范化和基于允许列表的常规操作的情况下,不建议使用JNDI和LDAP中具有特殊意义的字符。在添加到用于验证输入的允许列表表达式之前,必须将特殊字符转换为清理后的安全值。同样,在验证步骤之前应进行用户输入的规范化(来源:防止LDAP注入)。

The OWASP ESAPI Encoder API provides encodeForLDAP(String) for search-filter values and encodeForDN(String) for distinguished-name values. Choose the method for the LDAP context where the value will be used.

OWASP ESAPI Encoder API为搜索过滤器值提供encodeForLDAP(String),为区分名称值提供encodeForDN(String)。根据值将用于的LDAP上下文选择方法。

Insecure vs Secure Java LDAP Query Construction / 不安全与安全的Java LDAP查询构建

❌ Insecure Example (vulnerable to LDAP Injection)

❌ 不安全示例(易受LDAP注入攻击)

// User input directly concatenated into the filter
String filter = "(&(uid=" + userInput + ")(objectClass=person))";
NamingEnumeration<SearchResult> results =
    ctx.search("ou=users,dc=example,dc=com", filter, controls);


✅ Secure Example (using parameterized filter)

// User input safely passed as a parameter
String filter = "(&(uid={0})(objectClass=person))";
NamingEnumeration<SearchResult> results =
    ctx.search("ou=users,dc=example,dc=com", filter, new Object[]{ userInput }, controls);

Safe C Sharp .NET TBA Example / 安全的C# .NET TBA示例

.NET AntiXSS) (.NET 防止 XSS) (now the Encoder class) has LDAP encoding functions including Encoder.LdapFilterEncode(string), Encoder.LdapDistinguishedNameEncode(string) and Encoder.LdapDistinguishedNameEncode(string, bool, bool).

Encoder.LdapFilterEncode encodes input according to RFC4515 where unsafe values are converted to \XX where XX is the representation of the unsafe character.

Encoder.LdapFilterEncode 方法根据 RFC4515 对输入进行编码,将不安全值转换为 \XX,其中 XX 是不安全字符的表示形式。

Use Encoder.LdapDistinguishedNameEncode for distinguished-name attribute values. In DN string escaping, \XX represents an escaped byte using two hexadecimal digits; selected special characters can instead be prefixed with a backslash. Escape leading spaces or # and trailing spaces. A leading # followed by hexadecimal pairs represents an entire attribute value encoded using Basic Encoding Rules (BER), not individual character escapes.

使用 Encoder.LdapDistinguishedNameEncode 对区分名称属性值进行编码。在 DN 字符串转义 中,\XX 表示使用两个十六进制数字表示的转义字节;选定的特殊字符可以前面加上反斜杠。转义前导空格或 # 以及尾随空格。前导 # 后跟十六进制对表示使用基本编码规则 (BER) 编码的整个属性值,而不是单个字符的转义。

LdapDistinguishedNameEncode(string, bool, bool) is also provided so you may turn off the initial or final character escaping rules, for example if you are concatenating the escaped distinguished name fragment into the midst of a complete distinguished name.

LdapDistinguishedNameEncode(string, bool, bool) 方法也提供,以便您可以关闭初始或最终字符转义规则,例如,如果您正在将转义后的区分名称片段连接到完整区分名称的中间部分。

Defense Option 2: Use Frameworks that Automatically Protect from LDAP Injection / 防御选项 2:使用自动防止 LDAP 注入的框架

Safe .NET Example / 安全的 .NET 示例

We recommend using LINQ to LDAP (for .NET Framework 4.5 or lower until it has been updated) in DotNet. It provides automatic LDAP encoding when building LDAP queries. Contact the Readme file in the project repository.

我们建议在 DotNet 中使用 LINQ to LDAP(对于 .NET Framework 4.5 或更低版本 直到它被更新)。它提供在构建 LDAP 查询时的自动 LDAP 编码。请联系项目仓库中的 Readme 文件。

Additional Defenses / 其他防御措施

Beyond adopting one of the two primary defenses, we also recommend adopting all of these additional defenses in order to provide defense in depth. These additional defenses are:

采用两种主要防御手段之一的同时,我们还建议采用所有这些附加防御措施,以提供多层次防御。这些附加防御措施包括:

  • Least Privilege
    中文:最小权限**
  • Allow-List Input Validation
    中文:允许列表输入验证**

Least Privilege / 最小权限

To minimize the potential damage of a successful LDAP injection attack, you should minimize the privileges assigned to the LDAP binding account in your environment.

为了最小化成功LDAP注入攻击的潜在损害,您应该将环境中的LDAP绑定账户的权限降到最低。

Enabling Bind Authentication / 启用绑定认证

Authenticated LDAP bind establishes an authenticated authorization state; it does not prevent user input from changing a search filter constructed by the application. Continue to apply the primary injection defenses, including search-filter escaping, even when the application binds with valid credentials.

认证的LDAP绑定建立了一个认证授权状态;它并不能阻止用户输入更改应用程序构建的搜索过滤器。即使在应用程序使用有效凭据绑定时,也要继续应用主要注入防御,包括搜索过滤器转义。

When using name/password authentication, reject empty passwords before binding. A nonempty name with an empty password can perform an unauthenticated bind that establishes anonymous authorization; a successful result in that case does not prove the user's identity. Require authenticated access to protected directory data and use a least-privileged binding account.

在使用用户名/密码认证时,在绑定之前拒绝空密码。一个非空的用户名和空密码可以执行一个未认证的绑定,这会建立匿名授权;在这种情况下,成功的结果并不能证明用户的身份。要求对受保护目录数据进行认证访问,并使用最小权限的绑定账户。

Allow-List Input Validation / 允许列表输入验证

Input validation can be used to detect unauthorized input before it is passed to the LDAP query. For more information please see the Input Validation Cheat Sheet.

输入验证可以在将输入传递给LDAP查询之前检测未授权的输入。更多信息请参阅输入验证速查表。

References / 参考