跳到内容

LDAP 注入防护备忘单

简介

轻量级目录访问协议(LDAP)允许应用程序远程执行诸如在目录中搜索和修改记录等操作。LDAP 注入是由于输入净化和验证不足造成的,它允许恶意用户利用目录服务获取受限信息。有关 LDAP 的一般信息,请访问轻量级目录访问协议(LDAP)

LDAP 注入是一种攻击,用于利用基于用户输入构建 LDAP 语句的 Web 应用程序。当应用程序未能正确净化用户输入时,可以通过类似于SQL 注入的技术修改 LDAP 语句。

本备忘单旨在为防止应用程序中的 LDAP 注入缺陷提供清晰、简单、可操作的指导。LDAP 注入攻击因以下两个因素而常见:

  1. 缺乏更安全、参数化的 LDAP 查询接口
  2. LDAP 在用户系统认证中的广泛使用。

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

主要防御措施

  • 使用正确的 LDAP 编码函数转义所有变量
  • 使用可自动转义的框架。

额外防御措施

  • 最小权限原则
  • 白名单输入验证

主要防御措施

防御选项 1:使用正确的 LDAP 编码函数转义所有变量

分辨名转义

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

一个 DN 可能看起来像这样:

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

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

白名单可用于将输入限制为有效字符列表。必须从白名单中排除的字符和字符序列——包括 Java 命名和目录接口(JNDI)元字符和 LDAP 特殊字符——列在以下列表中。

完整列表如下:\ # + < > , ; " = 以及开头或结尾的空格。

一些在分辨名中允许且无需转义的“特殊”字符包括:

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

搜索过滤器转义

每个 DN 都精确指向 1 个条目,这可以看作是关系型数据库管理系统(RDBMS)中的一行。对于每个条目,将有一个或多个属性,这些属性类似于 RDBMS 中的列。如果您有兴趣在 LDAP 中搜索具有特定属性的用户,可以使用搜索过滤器进行。

在搜索过滤器中,您可以使用标准的布尔逻辑来获取符合任意约束的用户列表。搜索过滤器以波兰表示法(即前缀表示法)编写。

示例

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

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

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

* ( ) \ NUL

有关搜索过滤器转义的更多信息,请访问RFC4515

安全的 Java 转义示例

以下解决方案使用白名单来净化用户输入,以便过滤器字符串只包含有效字符。在此代码中,userSN 只能包含字母和空格,而密码只能包含字母和数字字符。

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

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

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

当数据库字段(例如密码)必须包含特殊字符时,至关重要的是确保真实数据以净化后的形式存储在数据库中,并且在验证或比较发生之前,任何用户输入都已标准化。在缺乏全面的规范化和基于白名单的例程的情况下,不鼓励使用在 JNDI 和 LDAP 中具有特殊含义的字符。特殊字符必须在添加到将用于验证输入的白名单表达式之前,转换为经过净化的安全值。同样,用户输入的规范化应在验证步骤之前进行(来源:Prevent LDAP injection)。

欲了解更多信息,请访问OWASP ESAPI Java 编码器项目,该项目包含 encodeForLDAP(String) 和 encodeForDN(String) 方法

安全的 C Sharp .NET TBA 示例

.NET AntiXSS(现为 Encoder 类)具有 LDAP 编码函数,包括 Encoder.LdapFilterEncode(string)Encoder.LdapDistinguishedNameEncode(string)Encoder.LdapDistinguishedNameEncode(string, bool, bool)

Encoder.LdapFilterEncode 根据 RFC4515 对输入进行编码,其中不安全的值被转换为 \XXXX 是不安全字符的表示。

Encoder.LdapDistinguishedNameEncode 根据 RFC2253 对输入进行编码,其中不安全字符被转换为 #XXXX 是不安全字符的表示,逗号、加号、引号、斜杠、小于号和大于号使用斜杠表示法 (\X) 进行转义。此外,输入字符串开头的空格或井号 (#) 以及字符串末尾的空格也会被 \ 转义。

还提供了 LdapDistinguishedNameEncode(string, bool, bool),以便您可以关闭初始或最终字符转义规则,例如,当您将转义后的分辨名片段连接到完整分辨名的中间时。

防御选项 2:使用自动保护免受 LDAP 注入的框架

安全的 .NET 示例

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

额外防御措施

除了采用两种主要防御措施之一外,我们还建议采用所有这些额外防御措施,以提供深度防御。这些额外防御措施包括:

  • 最小权限原则
  • 白名单输入验证

最小权限原则

为了最大程度地减少成功 LDAP 注入攻击可能造成的损害,您应在您的环境中最大限度地减少分配给 LDAP 绑定账户的权限。

启用绑定认证

如果 LDAP 协议配置了绑定认证,攻击者将无法执行 LDAP 注入攻击,因为会针对用户传递的有效凭据执行验证和授权检查。攻击者仍然可以通过匿名连接或利用未认证绑定来绕过绑定认证:匿名绑定(LDAP)和未认证绑定(LDAP)。

白名单输入验证

输入验证可用于在将未经授权的输入传递给 LDAP 查询之前检测它。更多信息请参阅输入验证备忘单