zl程序教程

您现在的位置是:首页 >  后端

当前栏目

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

JAVA编码注入程序 指南 建议 编写 防止
2023-09-11 14:17:35 时间

本节书摘来异步社区《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中有特殊含义字符的程序是极不安全的。特殊字符在被添加到白名单验证之前必须被转换为清洁的安全值。就像用户输入在被验证之前首先要被标准化一样。

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


编写Java程序模拟简单的计算器。(面向对象思想) 定义名为Number的类其中有两个整型数据成员n1和n2应声明为私有,编写构造方法赋予n1和n2初始值,再为该类定义加addition()、减subtration()、乘multiplication()、除division()等公有实例方法,分别对两个成员变量执行加、减、乘、除的运算。
异步社区 异步社区(www.epubit.com)是人民邮电出版社旗下IT专业图书旗舰社区,也是国内领先的IT专业图书社区,致力于优质学习内容的出版和分享,实现了纸书电子书的同步上架,于2015年8月上线运营。公众号【异步图书】,每日赠送异步新书。