LDAP 注入防护备忘单¶
简介¶
轻量级目录访问协议(LDAP)允许应用程序远程执行诸如在目录中搜索和修改记录等操作。LDAP 注入是由于输入净化和验证不足造成的,它允许恶意用户利用目录服务获取受限信息。有关 LDAP 的一般信息,请访问轻量级目录访问协议(LDAP)。
LDAP 注入是一种攻击,用于利用基于用户输入构建 LDAP 语句的 Web 应用程序。当应用程序未能正确净化用户输入时,可以通过类似于SQL 注入的技术修改 LDAP 语句。
本备忘单旨在为防止应用程序中的 LDAP 注入缺陷提供清晰、简单、可操作的指导。LDAP 注入攻击因以下两个因素而常见:
- 缺乏更安全、参数化的 LDAP 查询接口
- 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 对输入进行编码,其中不安全的值被转换为 \XX
,XX
是不安全字符的表示。
Encoder.LdapDistinguishedNameEncode
根据 RFC2253 对输入进行编码,其中不安全字符被转换为 #XX
,XX
是不安全字符的表示,逗号、加号、引号、斜杠、小于号和大于号使用斜杠表示法 (\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 查询之前检测它。更多信息请参阅输入验证备忘单。
相关文章¶
- OWASP 关于 LDAP 注入漏洞的文章。
- OWASP 测试指南中关于如何测试 LDAP 注入漏洞的文章。