《Java编码指南:编写安全可靠程序的75条建议》—— 指南9:防止LDAP注入

简介: 轻量级目录访问协议(Lightweight Directory Access Protocol,LDAP)允许应用程序执行远程操作,如在目录中搜索和修改记录。不足的输入处理(sanitization)和验证会导致LDAP注入攻击,并允许恶意用户通过使用目录服务来收集被限制的信息。

本节书摘来异步社区《Java编码指南:编写安全可靠程序的75条建议》一书中的第1章,第1.9节,作者:【美】Fred Long(弗雷德•朗), Dhruv Mohindra(德鲁•莫欣达), Robert C.Seacord(罗伯特 C.西科德), Dean F.Sutherland(迪恩 F.萨瑟兰), David Svoboda(大卫•斯沃博达),更多章节内容可以访问云栖社区“异步社区”公众号查看。

指南9:防止LDAP注入

轻量级目录访问协议(Lightweight Directory Access Protocol,LDAP)允许应用程序执行远程操作,如在目录中搜索和修改记录。不足的输入处理(sanitization)和验证会导致LDAP注入攻击,并允许恶意用户通过使用目录服务来收集被限制的信息。

白名单可以用来限制用户输入,使其符合一个有效的字符列表。禁止加入白名单的字符或字符序列包括:Java命名和目录接口(Java Naming and Directory Interface,JNDI)的元字符和LDAP特殊字符,这些字符都列在了表1-1中。

116de4cd03c9c130be81ee76a1263d6b8e3241a1

注:*这是一个字符序列。

LDAP注入示例

试想一个LDAP数据交换格式(LDAP Data Interchange Format,LDIF)文件,其中包含的记录格式如下:

dn: dc=example,dc=com
objectclass: dcobject
objectClass: organization
o: Some Name
dc: example

dn: ou=People,dc=example,dc=com
ou: People
objectClass: dcobject
objectClass: organizationalUnit
dc: example

dn: cn=Manager,ou=People,dc=example,dc=com
cn: Manager
sn: John Watson
# Several objectClass definitions here (omitted)
userPassword: secret1
mail: john@holmesassociates.com

dn: cn=Senior Manager,ou=People,dc=example,dc=com
cn: Senior Manager
sn: Sherlock Holmes
# Several objectClass definitions here (omitted)
userPassword: secret2
mail: sherlock@holmesassociates.com

并且对有效用户名和密码的搜索经常使用下面这种形式:

(&(sn=<USERSN>)(userPassword=<USERPASSWORD>))

然而,攻击者可以通过在USERSN字段中输入S、在USERPASSWORD字段中输入来绕过身份验证。这样的输入将会使所有在USERSN字段中以S开头的记录被悉数查出。

如果验证例程存在LDAP注入风险,就会导致未经验证的用户登录。同样地,有类似问题的搜索例程就会将该目录下部分甚至全部数据暴露给攻击者。

违规代码示例

下面的违规代码示例使用LDAP协议来允许调用者通过searchRecord()方法来搜索目录中的记录。匹配到调用者提供的用户名和密码后,字符串过滤器将会从结果集中过滤出匹配的结果。

// String userSN = "S*"; // Invalid
// String userPassword = "*"; // Invalid
public class LDAPInjection {
 private void searchRecord(String userSN, String userPassword)
   throws NamingException {
  Hashtable<String, String> env =
   new Hashtable<String, String>();
  env.put(Context.INITIAL_CONTEXT_FACTORY,
      "com.sun.jndi.ldap.LdapCtxFactory");
  try {
   DirContext dctx = new InitialDirContext(env);

   SearchControls sc = new SearchControls();
   String[] attributeFilter = {"cn", "mail"};
   sc.setReturningAttributes(attributeFilter);
   sc.setSearchScope(SearchControls.SUBTREE_SCOPE);
   String base = "dc=example,dc=com";
   // The following resolves to (&(sn=S*)(userPassword=*))
   String filter = "(&(sn=" + userSN + ")(userPassword=" +
           userPassword + "))";
   NamingEnumeration<?> results =
    dctx.search(base, filter, sc);
   while (results.hasMore()) {
    SearchResult sr = (SearchResult) results.next();
    Attributes attrs = (Attributes) sr.getAttributes();
    Attribute attr = (Attribute) attrs.get("cn");
    System.out.println(attr);
    attr = (Attribute) attrs.get("mail");
    System.out.println(attr);
   }
   dctx.close();
  } catch (NamingException e) {
   // Forward to handler
  }
 }
}

当恶意用户输入别有用心的值时,正如前面提到的,这个基本的身份验证方案没能对需要用户访问特权的信息查询作出限制。

合规解决方案

下面的合规解决方案使用白名单对用户输入进行无害化处理,使得filter字符串只包含有效字符。在这段代码中,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中有特殊含义字符的程序是极不安全的。特殊字符在被添加到白名单验证之前必须被转换为清洁的安全值。就像用户输入在被验证之前首先要被标准化一样。

适用性

未能对不可信输入做无害化处理可能导致信息泄露和特权升级。

相关文章
|
18天前
|
消息中间件 分布式计算 Java
Linux环境下 java程序提交spark任务到Yarn报错
Linux环境下 java程序提交spark任务到Yarn报错
84 4
|
22天前
|
安全 Java
Java异常处理:程序世界的“交通规则
Java异常处理:程序世界的“交通规则
257 98
|
1月前
|
存储 Java 编译器
对比Java学习Go——程序结构与变量
本节对比了Java与Go语言的基础结构,包括“Hello, World!”程序、代码组织方式、入口函数定义、基本数据类型及变量声明方式。Java强调严格的面向对象结构,所有代码需置于类中,入口方法需严格符合`public static void main(String[] args)`格式;而Go语言结构更简洁,使用包和函数组织代码,入口函数为`func main()`。两种语言在变量声明、常量定义、类型系统等方面也存在显著差异,体现了各自的设计哲学。
|
2月前
|
人工智能 监控 安全
智慧工地解决方案,java智慧工地程序代码
智慧工地系统融合物联网、AI、大数据等技术,实现对施工现场“人、机、料、法、环”的全面智能监控与管理,提升安全、效率与决策水平。
|
7月前
|
存储 Java 数据库连接
【YashanDB知识库】Java程序调用存储过程,在提取clob时报YAS-00004
【YashanDB知识库】Java程序调用存储过程,在提取clob时报YAS-00004
|
7月前
|
搜索推荐 Java Android开发
课时146:使用JDT开发Java程序
在 Eclipse 之中提供有 JDT环境可以实现java 程序的开发,下面就通过一些功能进行演示。 项目开发流程
270 0
|
7月前
|
Java C语言
课时8:Java程序基本概念(标识符与关键字)
课时8介绍Java程序中的标识符与关键字。标识符由字母、数字、下划线和美元符号组成,不能以数字开头且不能使用Java保留字。建议使用有意义的命名,如student_name、age。关键字是特殊标记,如蓝色字体所示。未使用的关键字有goto、const;特殊单词null、true、false不算关键字。JDK1.4后新增assert,JDK1.5后新增enum。
122 4
|
Java 数据安全/隐私保护 网络协议
|
19天前
|
JSON 网络协议 安全
【Java】(10)进程与线程的关系、Tread类;讲解基本线程安全、网络编程内容;JSON序列化与反序列化
几乎所有的操作系统都支持进程的概念,进程是处于运行过程中的程序,并且具有一定的独立功能,进程是系统进行资源分配和调度的一个独立单位一般而言,进程包含如下三个特征。独立性动态性并发性。
85 1
|
19天前
|
JSON 网络协议 安全
【Java基础】(1)进程与线程的关系、Tread类;讲解基本线程安全、网络编程内容;JSON序列化与反序列化
几乎所有的操作系统都支持进程的概念,进程是处于运行过程中的程序,并且具有一定的独立功能,进程是系统进行资源分配和调度的一个独立单位一般而言,进程包含如下三个特征。独立性动态性并发性。
73 1